Added Android UWB code

This commit is contained in:
2019-04-02 18:48:48 +02:00
parent 799e563ff5
commit 5a7507a93e
82 changed files with 12323 additions and 137 deletions

View File

@@ -9,37 +9,56 @@
#include <sstream> #include <sstream>
#include <chrono> #include <chrono>
#include <iomanip> #include <iomanip>
#include <thread>>
#include <QStandardPaths> #include <QStandardPaths>
#include <QtGlobal> #include <QtGlobal>
#include <QDir> #include <QDir>
#include <QTextStream>
#include "uwb.h"
const std::string NUC1 = "38:de:ad:6d:77:25"; const std::string NUC1 = "38:de:ad:6d:77:25";
const std::string NUC2 = "38:de:ad:6d:60:ff"; const std::string NUC2 = "38:de:ad:6d:60:ff";
const std::string NUC3 = "1c:1b:b5:ef:a2:9a"; const std::string NUC3 = "1c:1b:b5:ef:a2:9a";
const std::string NUC4 = "1c:1b:b5:ec:d1:82"; const std::string NUC4 = "1c:1b:b5:ec:d1:82";
static long long startTime = 0;
static QString GetCurrentTimeForFileName() static QString GetCurrentTimeForFileName()
{ {
auto time = std::time(nullptr); auto time = std::time(nullptr);
std::stringstream ss; std::stringstream ss;
ss << std::put_time(std::localtime(&time), "%F_%T"); // ISO 8601 without timezone information. ss << std::put_time(std::localtime(&time), "%Y%m%d_%H%M%S");
auto s = ss.str(); auto s = ss.str();
std::replace(s.begin(), s.end(), ':', '-'); std::replace(s.begin(), s.end(), ':', '-');
return QString::fromStdString(s); return QString::fromStdString(s);
} }
static long long nowInMsec()
{
auto now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
return millis;
}
Manager::Manager() { Manager::Manager() {
} }
void Manager::trigger() { void Manager::trigger() {
QString folder = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/ftm/"; QString folder = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/ftm/";
dataLogger = std::make_shared<QFile>(folder+"/ftm_"+GetCurrentTimeForFileName()+".txt"); QString prefix = GetCurrentTimeForFileName();
dataLogger = std::make_shared<QFile>(folder+"/"+prefix+"_ftm.txt");
uwbLogger = std::make_shared<QFile>(folder+"/"+prefix+"_uwb.txt");
gtLogger = std::make_shared<QFile>(folder+"/"+prefix+"_gt.txt");
if (!dataLogger->exists()) { if (!dataLogger->exists()) {
// create the folder, if necessary // create the folder, if necessary
@@ -57,10 +76,24 @@ void Manager::trigger() {
dataLogger = nullptr; dataLogger = nullptr;
} }
if (!uwbLogger->open(QIODevice::ReadWrite)) {
qWarning() << "Failed to create uwb data logger file" << uwbLogger->fileName();
uwbLogger = nullptr;
}
if (!gtLogger->open(QIODevice::ReadWrite)) {
qWarning() << "Failed to create gt data logger file" << gtLogger->fileName();
gtLogger = nullptr;
}
startTime = nowInMsec();
#ifdef ANDROID #ifdef ANDROID
QAndroidJniObject::callStaticMethod<int>("android/net/wifi/UWB", "start", "()I");
QAndroidJniObject::callStaticMethod<int>("android/net/wifi/RTT", "start", "()I"); QAndroidJniObject::callStaticMethod<int>("android/net/wifi/RTT", "start", "()I");
#else #else
//onData("38:de:ad:6d:77:25;FAILED"); //onData("38:de:ad:6d:77:25;FAILED");
@@ -75,21 +108,39 @@ void Manager::trigger() {
void Manager::stop() { void Manager::stop() {
#ifdef ANDROID #ifdef ANDROID
QAndroidJniObject::callStaticMethod<int>("android/net/wifi/RTT", "stop", "()I"); QAndroidJniObject::callStaticMethod<int>("android/net/wifi/RTT", "stop", "()I");
QAndroidJniObject::callStaticMethod<int>("android/net/wifi/UWB", "stop", "()I");
#endif #endif
dataLogger->flush(); dataLogger->flush();
dataLogger->close(); dataLogger->close();
uwbLogger->flush();
uwbLogger->close();
gtLogger->flush();
gtLogger->close();
}
void Manager::manualCheckpoint() {
qDebug() << "Manual checkpoint";
long long timestamp = nowInMsec();
timestamp -= startTime;
QTextStream out(gtLogger.get());
out << timestamp << endl;
} }
void Manager::onData(std::string str) { void Manager::onData(std::string str) {
qDebug() << QString(str.c_str()); qDebug() << QString(str.c_str());
if (dataLogger) { long long timestamp = nowInMsec();
dataLogger->write(str.c_str()); timestamp -= startTime;
dataLogger->write("\n");
}
bool successfullMeas = true;
std::stringstream lineStream(str); std::stringstream lineStream(str);
std::string cell; std::string cell;
@@ -99,47 +150,111 @@ void Manager::onData(std::string str) {
int i = 0; int i = 0;
const float alpha = 0.7f; const float alpha = 0.7f;
QTextStream out(dataLogger.get());
out << timestamp << ";";
while (std::getline(lineStream, cell, ';')) { while (std::getline(lineStream, cell, ';')) {
switch(i) { switch(i) {
case 0: { case 0: {
// success flag
successfullMeas = (cell == "1");
out << (successfullMeas ? 1 : 0) << ";";
break;
}
case 1: {
// timestamp; ignore;
break; break;
} }
case 1: { case 2: {
if(NUC1 == cell) {distIndex = 0;} if(NUC1 == cell) {distIndex = 0;}
if(NUC2 == cell) {distIndex = 1;} if(NUC2 == cell) {distIndex = 1;}
if(NUC3 == cell) {distIndex = 2;} if(NUC3 == cell) {distIndex = 2;}
if(NUC4 == cell) {distIndex = 3;} if(NUC4 == cell) {distIndex = 3;}
out << QString(cell.c_str()) << ";";
break; break;
} }
case 2: { case 3: {
if ("FAILED" == cell) { if (successfullMeas) {
_dist[distIndex] = 0; _dist[distIndex] = std::stoi(cell) + _offset;
//_dist[distIndex] = _dist[distIndex] * alpha + atoi(cell.c_str()) * (1-alpha);
} else { } else {
//_dist[distIndex] = std::stoi(cell); _dist[distIndex] = 0;
_dist[distIndex] = _dist[distIndex] * alpha + atoi(cell.c_str()) * (1-alpha);
} }
out << _dist[distIndex] << ";";
break; break;
} }
case 3: { case 4: {
_stdDev[distIndex] = atoi(cell.c_str()); if (successfullMeas) {
_stdDev[distIndex] = atoi(cell.c_str());
} else {
_stdDev[distIndex] = 0;
}
out << _stdDev[distIndex] << ";";
break; break;
} }
case 4: { case 5: {
// RSSI // RSSI
}
out << cell.c_str() << ";";
break;
}
} }
++i; ++i;
} }
emit distChanged(); out << endl;
// if (dataLogger && successfullMeas) {
// dataLogger->write(str.c_str());
// dataLogger->write("\n");
// }
emit distChanged();
}
void Manager::onUWBData(std::vector<uchar> data) {
qDebug() << "Received uwb data " << data;
long long timestamp = nowInMsec();
timestamp -= startTime;
UwbResult uwbResult = uwb_parse(data.data());
_uwbDist[0] = 0;
_uwbDist[1] = 0;
_uwbDist[2] = 0;
_uwbDist[3] = 0;
for (UwbDistance& dist : uwbResult.distances) {
qDebug() << dist.nodeID;
switch (dist.nodeID) {
case 0x1D8C: _uwbDist[0] = static_cast<float>(dist.distance); break;
case 0x47A7: _uwbDist[1] = static_cast<float>(dist.distance); break;
case 0x863B: _uwbDist[2] = static_cast<float>(dist.distance); break;
case 0x58B4: _uwbDist[3] = static_cast<float>(dist.distance); break;
}
if (uwbLogger) {
QTextStream out(uwbLogger.get());
out << timestamp << ";" << "1;" << "DW" << hex << dist.nodeID << dec << ";" << dist.distance << endl;
}
}
emit distChanged();
} }
Manager mgmt; Manager mgmt;
@@ -156,5 +271,16 @@ extern "C" {
mgmt.onData(str); mgmt.onData(str);
} }
JNIEXPORT void JNICALL Java_android_net_wifi_UWB_onUWBComplete(JNIEnv* env, jobject jobj, jbyteArray arrayID) {
(void) env; (void) jobj;
jsize length = env->GetArrayLength(arrayID);
jbyte* data = env->GetByteArrayElements(arrayID, 0);
std::vector<uchar> c_data;
c_data.assign(reinterpret_cast<char*>(data), reinterpret_cast<char*>(data)+length);
env->ReleaseByteArrayElements(arrayID, data, JNI_ABORT);
mgmt.onUWBData(c_data);
}
} }
#endif #endif

View File

@@ -12,8 +12,13 @@ private:
float _dist[4]; float _dist[4];
float _stdDev[4]; float _stdDev[4];
float _offset = 500;
float _uwbDist[4];
std::shared_ptr<QFile> dataLogger; std::shared_ptr<QFile> dataLogger;
std::shared_ptr<QFile> uwbLogger;
std::shared_ptr<QFile> gtLogger;
public: public:
@@ -27,11 +32,22 @@ public:
Q_PROPERTY(float stdDev3 READ getStdDev3() NOTIFY distChanged) Q_PROPERTY(float stdDev3 READ getStdDev3() NOTIFY distChanged)
Q_PROPERTY(float stdDev4 READ getStdDev4() NOTIFY distChanged) Q_PROPERTY(float stdDev4 READ getStdDev4() NOTIFY distChanged)
// FTM offset
Q_PROPERTY(float offset READ getOffset() WRITE setOffset() NOTIFY offsetChanged)
Q_PROPERTY(float uwbDist1 READ getUwbDist1() NOTIFY distChanged)
Q_PROPERTY(float uwbDist2 READ getUwbDist2() NOTIFY distChanged)
Q_PROPERTY(float uwbDist3 READ getUwbDist3() NOTIFY distChanged)
Q_PROPERTY(float uwbDist4 READ getUwbDist4() NOTIFY distChanged)
Q_INVOKABLE void trigger(); Q_INVOKABLE void trigger();
Q_INVOKABLE void stop(); Q_INVOKABLE void stop();
Q_INVOKABLE void manualCheckpoint();
void onData(std::string str); void onData(std::string str);
void onUWBData(std::vector<uchar> data);
public: public:
@@ -45,9 +61,18 @@ public:
float getStdDev3() {return _stdDev[2];} float getStdDev3() {return _stdDev[2];}
float getStdDev4() {return _stdDev[3];} float getStdDev4() {return _stdDev[3];}
float getOffset() {return _offset;}
void setOffset(float value) { _offset = value; emit offsetChanged(); }
float getUwbDist1() {return _uwbDist[0];}
float getUwbDist2() {return _uwbDist[1];}
float getUwbDist3() {return _uwbDist[2];}
float getUwbDist4() {return _uwbDist[3];}
signals: signals:
void distChanged(); void distChanged();
void offsetChanged();
public: public:

80
RTT.pro
View File

@@ -46,4 +46,82 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target !isEmpty(target.path): INSTALLS += target
HEADERS += \ HEADERS += \
Manager.h Manager.h \
uwb.h
DISTFILES += \
_android/src/UWB.java \
_android/src/no/nordicsemi/android/ble/annotation/ConnectionPriority.java \
_android/src/no/nordicsemi/android/ble/annotation/ConnectionState.java \
_android/src/no/nordicsemi/android/ble/annotation/PhyMask.java \
_android/src/no/nordicsemi/android/ble/annotation/PhyOption.java \
_android/src/no/nordicsemi/android/ble/annotation/PhyValue.java \
_android/src/no/nordicsemi/android/ble/annotation/WriteType.java \
_android/src/no/nordicsemi/android/ble/callback/profile/ProfileDataCallback.java \
_android/src/no/nordicsemi/android/ble/callback/profile/ProfileReadResponse.java \
_android/src/no/nordicsemi/android/ble/callback/BeforeCallback.java \
_android/src/no/nordicsemi/android/ble/callback/ConnectionPriorityCallback.java \
_android/src/no/nordicsemi/android/ble/callback/DataReceivedCallback.java \
_android/src/no/nordicsemi/android/ble/callback/DataSentCallback.java \
_android/src/no/nordicsemi/android/ble/callback/FailCallback.java \
_android/src/no/nordicsemi/android/ble/callback/InvalidRequestCallback.java \
_android/src/no/nordicsemi/android/ble/callback/MtuCallback.java \
_android/src/no/nordicsemi/android/ble/callback/PhyCallback.java \
_android/src/no/nordicsemi/android/ble/callback/ReadProgressCallback.java \
_android/src/no/nordicsemi/android/ble/callback/RssiCallback.java \
_android/src/no/nordicsemi/android/ble/callback/SuccessCallback.java \
_android/src/no/nordicsemi/android/ble/callback/WriteProgressCallback.java \
_android/src/no/nordicsemi/android/ble/data/Data.java \
_android/src/no/nordicsemi/android/ble/data/DataFilter.java \
_android/src/no/nordicsemi/android/ble/data/DataMerger.java \
_android/src/no/nordicsemi/android/ble/data/DataSplitter.java \
_android/src/no/nordicsemi/android/ble/data/DataStream.java \
_android/src/no/nordicsemi/android/ble/data/DefaultMtuSplitter.java \
_android/src/no/nordicsemi/android/ble/data/MutableData.java \
_android/src/no/nordicsemi/android/ble/error/GattError.java \
_android/src/no/nordicsemi/android/ble/exception/BluetoothDisabledException.java \
_android/src/no/nordicsemi/android/ble/exception/ConnectionException.java \
_android/src/no/nordicsemi/android/ble/exception/DeviceDisconnectedException.java \
_android/src/no/nordicsemi/android/ble/exception/InvalidDataException.java \
_android/src/no/nordicsemi/android/ble/exception/InvalidRequestException.java \
_android/src/no/nordicsemi/android/ble/exception/RequestFailedException.java \
_android/src/no/nordicsemi/android/ble/response/ConnectionPriorityResponse.java \
_android/src/no/nordicsemi/android/ble/response/MtuResult.java \
_android/src/no/nordicsemi/android/ble/response/PhyResult.java \
_android/src/no/nordicsemi/android/ble/response/ReadResponse.java \
_android/src/no/nordicsemi/android/ble/response/RssiResult.java \
_android/src/no/nordicsemi/android/ble/response/WriteResponse.java \
_android/src/no/nordicsemi/android/ble/utils/ILogger.java \
_android/src/no/nordicsemi/android/ble/utils/ParserUtils.java \
_android/src/no/nordicsemi/android/ble/BleManager.java \
_android/src/no/nordicsemi/android/ble/BleManagerCallbacks.java \
_android/src/no/nordicsemi/android/ble/ConnectionPriorityRequest.java \
_android/src/no/nordicsemi/android/ble/ConnectRequest.java \
_android/src/no/nordicsemi/android/ble/DisconnectRequest.java \
_android/src/no/nordicsemi/android/ble/MainThreadBluetoothGattCallback.java \
_android/src/no/nordicsemi/android/ble/MtuRequest.java \
_android/src/no/nordicsemi/android/ble/Operation.java \
_android/src/no/nordicsemi/android/ble/PhyRequest.java \
_android/src/no/nordicsemi/android/ble/ReadRequest.java \
_android/src/no/nordicsemi/android/ble/ReadRssiRequest.java \
_android/src/no/nordicsemi/android/ble/ReliableWriteRequest.java \
_android/src/no/nordicsemi/android/ble/Request.java \
_android/src/no/nordicsemi/android/ble/RequestQueue.java \
_android/src/no/nordicsemi/android/ble/SimpleRequest.java \
_android/src/no/nordicsemi/android/ble/SimpleValueRequest.java \
_android/src/no/nordicsemi/android/ble/SleepRequest.java \
_android/src/no/nordicsemi/android/ble/TimeoutableRequest.java \
_android/src/no/nordicsemi/android/ble/TimeoutableValueRequest.java \
_android/src/no/nordicsemi/android/ble/TimeoutHandler.java \
_android/src/no/nordicsemi/android/ble/ValueChangedCallback.java \
_android/src/no/nordicsemi/android/ble/WaitForValueChangedRequest.java \
_android/src/no/nordicsemi/android/ble/WriteRequest.java \
_android/src/DecaManager.java \
_android/src/DecaManagerCallbacks.java \
_android/AndroidManifest.xml \
_android/gradle/wrapper/gradle-wrapper.jar \
_android/gradlew \
_android/res/values/libs.xml \
_android/build.gradle \
_android/gradle/wrapper/gradle-wrapper.properties \
_android/gradlew.bat

View File

@@ -1,49 +1,49 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<manifest package="indoor.java" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto"> <manifest package="indoor.java" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<uses-sdk android:minSdkVersion="26"/> <uses-sdk android:minSdkVersion="28"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.BODY_SENSORS"/> <uses-permission android:name="android.permission.BODY_SENSORS"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="@string/app_name"> <application android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="@string/app_name">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="indoor.java.MyActivity" android:label="TEST" android:screenOrientation="unspecified"> <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="indoor.java.MyActivity" android:label="TEST" android:screenOrientation="unspecified">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/> <meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/> <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/> <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/> <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/> <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/> <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/> <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/> <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/> <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/> <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/> <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
</activity> </activity>
</application> </application>
</manifest> </manifest>

65
_android/build.gradle Normal file
View File

@@ -0,0 +1,65 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
}
}
repositories {
google()
jcenter()
}
apply plugin: 'com.android.application'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'androidx.annotation:annotation:1.0.0'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qt5AndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qt5AndroidDir + '/res', 'res']
resources.srcDirs = ['src']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
lintOptions {
abortOnError false
}
}

View File

@@ -0,0 +1,18 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.enableJetifier=true
android.useAndroidX=true

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
_android/gradlew vendored Normal file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
_android/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,25 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<array name="qt_sources">
<item>https://download.qt.io/ministro/android/qt5/qt-5.9</item>
</array>
<!-- The following is handled automatically by the deployment tool. It should
not be edited manually. -->
<array name="bundled_libs">
<!-- %%INSERT_EXTRA_LIBS%% -->
</array>
<array name="qt_libs">
<!-- %%INSERT_QT_LIBS%% -->
</array>
<array name="bundled_in_lib">
<!-- %%INSERT_BUNDLED_IN_LIB%% -->
</array>
<array name="bundled_in_assets">
<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->
</array>
</resources>

View File

@@ -0,0 +1,107 @@
package com.example.nrftest;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.util.UUID;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.BleManager;
import no.nordicsemi.android.ble.callback.profile.ProfileDataCallback;
import no.nordicsemi.android.ble.data.Data;
public class DecaManager extends BleManager<DecaManagerCallbacks> {
private static final String TAG = "DecaManager";
/** Decawave Service UUID. */
public final static UUID LBS_UUID_SERVICE = UUID.fromString("680c21d9-c946-4c1f-9c11-baa1c21329e7");
/** */
public final static UUID LBS_UUID_LOCATION_DATA_CHAR = UUID.fromString("003bbdf2-c634-4b3d-ab56-7ec889b89a37");
private BluetoothGattCharacteristic _locationDataCharacteristic;
public DecaManager(@NonNull final Context context)
{
super(context);
}
@NonNull
@Override
protected BleManagerGattCallback getGattCallback() {
return mGattCallback;
}
/**
* BluetoothGatt callbacks object.
*/
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
@Override
protected void initialize() {
requestMtu(512).enqueue();
setNotificationCallback(_locationDataCharacteristic).with(mButtonCallback);
// readCharacteristic(mLedCharacteristic).with(mLedCallback).enqueue();
// readCharacteristic(mButtonCharacteristic).with(mButtonCallback).enqueue();
enableNotifications(_locationDataCharacteristic).enqueue();
}
@Override
public boolean isRequiredServiceSupported(@NonNull final BluetoothGatt gatt) {
final BluetoothGattService service = gatt.getService(LBS_UUID_SERVICE);
if (service != null) {
_locationDataCharacteristic = service.getCharacteristic(LBS_UUID_LOCATION_DATA_CHAR);
}
// boolean writeRequest = false;
// if (mLedCharacteristic != null) {
// final int rxProperties = mLedCharacteristic.getProperties();
// writeRequest = (rxProperties & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0;
// }
//
// mSupported = mButtonCharacteristic != null && mLedCharacteristic != null && writeRequest;
// return mSupported;
return _locationDataCharacteristic != null;
}
@Override
protected void onDeviceDisconnected() {
_locationDataCharacteristic = null;
}
};
private final ProfileDataCallback mButtonCallback = new ProfileDataCallback() {
@Override
public void onDataReceived(@NonNull BluetoothDevice device, @NonNull Data data) {
Log.d(TAG, "onDataReceived: length=" + data.size() + " data=" + data);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
for (int i = 0; i < data.size(); i++) {
stream.write(data.getByte(i));
}
mCallbacks.onTagLocationData(stream.toByteArray());
}
@Override
public void onInvalidDataReceived(@NonNull final BluetoothDevice device,
@NonNull final Data data) {
Log.w(TAG, "Invalid data received: " + data);
}
};
}

View File

@@ -0,0 +1,8 @@
package com.example.nrftest;
import no.nordicsemi.android.ble.BleManagerCallbacks;
public interface DecaManagerCallbacks extends BleManagerCallbacks {
void onTagLocationData(byte[] dataStr);
}

View File

@@ -62,10 +62,8 @@ public class RTT {
Log.d("RTT", mac.toString() + " FAILED"); Log.d("RTT", mac.toString() + " FAILED");
} }
RTT.onRTTComplete(serialize(res)); RTT.onRTTComplete(serialize(res));
} }
} }
}; };
@@ -75,28 +73,31 @@ public class RTT {
char delim = ';'; char delim = ';';
boolean success = res.getStatus() == RangingResult.STATUS_SUCCESS; boolean success = res.getStatus() == RangingResult.STATUS_SUCCESS;
if (success) { baos.write(success ? '1' : '0');
baos.write(("" + res.getRangingTimestampMillis()).getBytes()); baos.write(delim);
baos.write(delim);
} else { baos.write(("" + System.currentTimeMillis()).getBytes());
baos.write(("" + System.currentTimeMillis()).getBytes()); baos.write(delim);
baos.write(delim);
}
baos.write(res.getMacAddress().toString().getBytes()); baos.write(res.getMacAddress().toString().getBytes());
baos.write(delim); baos.write(delim);
if (success) { int distValue = 0;
baos.write( ("" + res.getDistanceMm()).getBytes() ); int distStdDev = 0;
baos.write(delim); int rssi = 0;
baos.write( ("" + res.getDistanceStdDevMm()).getBytes() );
baos.write(delim);
baos.write( ("" + res.getRssi()).getBytes() );
} else { if (success) {
baos.write( "FAILED".getBytes() ); distValue = res.getDistanceMm();
distStdDev = res.getDistanceStdDevMm();
rssi = res.getRssi();
} }
baos.write( ("" + distValue).getBytes() );
baos.write(delim);
baos.write( ("" + distStdDev).getBytes() );
baos.write(delim);
baos.write( ("" + rssi).getBytes() );
} catch (final Exception e) {;} } catch (final Exception e) {;}
return baos.toByteArray(); return baos.toByteArray();
} }

174
_android/src/UWB.java Normal file
View File

@@ -0,0 +1,174 @@
package android.net.wifi;
import android.app.Activity;
import android.util.Log;
import android.content.Context;
import java.lang.System;
import java.util.List;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import java.util.concurrent.Executor;
import android.net.MacAddress;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Array;
import indoor.java.MyActivity;
import java.io.ByteArrayOutputStream;
import com.example.nrftest.*;
public class UWB {
private static Activity act;
private static Executor mainExecutor;
private static Thread uwbThread;
private static boolean uwbRunning;
private static BluetoothManager bluetoothManager;
private static BluetoothAdapter bluetoothAdapter;
private static BluetoothDevice bluetoothDevice;
private static DecaManager uwbManager;
// called when a UWB is completed successfully
public static native void onUWBComplete(final byte[] result);
public static int start() {
if (uwbRunning)
return 0;
Log.d("UWB", "start()");
MyActivity act = MyActivity.act;
mainExecutor = act.getMainExecutor();
bluetoothManager = (BluetoothManager) act.getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothDevice = bluetoothAdapter.getRemoteDevice("D1:6C:7A:99:57:71");
if (bluetoothDevice == null) {
return 1;
}
uwbManager = new DecaManager(act.getApplicationContext());
uwbManager.setGattCallbacks(decaCallbacks);
uwbManager.connect(bluetoothDevice)
.retry(3, 100)
.useAutoConnect(false)
.enqueue();
uwbRunning = true;
// uwbThread = new Thread() {
// public void run() {
// while(uwbRunning) {
// try {
// Thread.sleep(200);
// } catch (Exception e) {;}
// startRangingOnMacs(macs);
// }
// }
// };
// uwbThread.start();
return 1337;
}
public static int stop() {
Log.d("UWB", "stop()");
if (uwbRunning) {
uwbManager.disconnect().enqueue();
uwbRunning = false;
}
return 1337*2;
}
static final DecaManagerCallbacks decaCallbacks = new DecaManagerCallbacks(){
@Override
public void onTagLocationData(byte[] data) {
onUWBComplete(data);
}
@Override
public void onDeviceConnecting(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onDeviceConnecting");
}
@Override
public void onDeviceConnected(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onDeviceConnected");
}
@Override
public void onDeviceDisconnecting(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onDeviceDisconnecting");
}
@Override
public void onDeviceDisconnected(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onDeviceDisconnected");
}
@Override
public void onLinkLossOccurred(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onLinkLossOccurred");
}
@Override
public void onServicesDiscovered(@NonNull BluetoothDevice device, boolean optionalServicesFound) {
Log.d("DecaCallbacks", "onServicesDiscovered");
}
@Override
public void onDeviceReady(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onDeviceReady");
}
@Override
public void onBondingRequired(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onBondingRequired");
}
@Override
public void onBonded(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onBonded");
}
@Override
public void onBondingFailed(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onBondingFailed");
}
@Override
public void onError(@NonNull BluetoothDevice device, @NonNull String message, int errorCode) {
Log.d("DecaCallbacks", "onError: " + message);
}
@Override
public void onDeviceNotSupported(@NonNull BluetoothDevice device) {
Log.d("DecaCallbacks", "onDeviceNotSupported");
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,206 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
// DO NOT EDIT THIS FILE UNLESS NECESSARY!
/**
* The BleManagerCallbacks should be overridden in your app and all the 'high level' callbacks
* should be added there. See examples in Android nRF Blinky or Android nRF Toolbox.
* <p>
* Keeping this file as is (and {@link BleManager} as well) will allow to quickly update it when
* an update is posted here.
*/
@SuppressWarnings({"DeprecatedIsStillUsed", "unused"})
public interface BleManagerCallbacks {
/**
* Called when the Android device started connecting to given device.
* The {@link #onDeviceConnected(BluetoothDevice)} will be called when the device is connected,
* or {@link #onError(BluetoothDevice, String, int)} in case of error.
*
* @param device the device that got connected.
*/
void onDeviceConnecting(@NonNull final BluetoothDevice device);
/**
* Called when the device has been connected. This does not mean that the application may start
* communication.
* A service discovery will be handled automatically after this call. Service discovery
* may ends up with calling {@link #onServicesDiscovered(BluetoothDevice, boolean)} or
* {@link #onDeviceNotSupported(BluetoothDevice)} if required services have not been found.
*
* @param device the device that got connected.
*/
void onDeviceConnected(@NonNull final BluetoothDevice device);
/**
* Called when user initialized disconnection.
*
* @param device the device that gets disconnecting.
*/
void onDeviceDisconnecting(@NonNull final BluetoothDevice device);
/**
* Called when the device has disconnected (when the callback returned
* {@link BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)} with state
* DISCONNECTED), but ONLY if the {@link BleManager#shouldAutoConnect()} method returned false
* for this device when it was connecting.
* Otherwise the {@link #onLinkLossOccurred(BluetoothDevice)} method will be called instead.
*
* @param device the device that got disconnected.
*/
void onDeviceDisconnected(@NonNull final BluetoothDevice device);
/**
* This callback is invoked when the Ble Manager lost connection to a device that has been
* connected with autoConnect option (see {@link BleManager#shouldAutoConnect()}.
* Otherwise a {@link #onDeviceDisconnected(BluetoothDevice)} method will be called on such
* event.
*
* @param device the device that got disconnected due to a link loss.
*/
void onLinkLossOccurred(@NonNull final BluetoothDevice device);
/**
* Called when service discovery has finished and primary services has been found.
* This method is not called if the primary, mandatory services were not found during service
* discovery. For example in the Blood Pressure Monitor, a Blood Pressure service is a
* primary service and Intermediate Cuff Pressure service is a optional secondary service.
* Existence of battery service is not notified by this call.
* <p>
* After successful service discovery the service will initialize all services.
* The {@link #onDeviceReady(BluetoothDevice)} method will be called when the initialization
* is complete.
*
* @param device the device which services got disconnected.
* @param optionalServicesFound if <code>true</code> the secondary services were also found
* on the device.
*/
void onServicesDiscovered(@NonNull final BluetoothDevice device, final boolean optionalServicesFound);
/**
* Method called when all initialization requests has been completed.
*
* @param device the device that get ready.
*/
void onDeviceReady(@NonNull final BluetoothDevice device);
/**
* This method should return true if Battery Level notifications should be enabled on the
* target device. If there is no Battery Service, or the Battery Level characteristic does
* not have NOTIFY property, this method will not be called for this device.
* <p>
* This method may return true only if an activity is bound to the service (to display the
* information to the user), always (e.g. if critical battery level is reported using
* notifications) or never, if such information is not important or the manager wants to
* control Battery Level notifications on its own.
*
* @param device the target device.
* @return True to enabled battery level notifications after connecting to the device,
* false otherwise.
* @deprecated Use
* <pre>{@code
* setNotificationCallback(batteryLevelCharacteristic)
* .with(new BatteryLevelDataCallback() {
* onBatteryLevelChanged(int batteryLevel) {
* ...
* }
* });
* }</pre>
* in the {@link BleManager.BleManagerGattCallback#initialize() initialize(BluetoothDevice)}
* instead.
*/
@Deprecated
default boolean shouldEnableBatteryLevelNotifications(@NonNull final BluetoothDevice device) {
return false;
}
/**
* Called when battery value has been received from the device.
*
* @param device the device from which the battery value has changed.
* @param value the battery value in percent.
* @deprecated Use
* <pre>{@code
* setNotificationCallback(batteryLevelCharacteristic)
* .with(new BatteryLevelDataCallback() {
* onBatteryLevelChanged(int batteryLevel) {
* ...
* }
* });
* }</pre>
* in the {@link BleManager.BleManagerGattCallback#initialize() initialize(BluetoothDevice)}
* instead.
*/
@Deprecated
default void onBatteryValueReceived(@NonNull final BluetoothDevice device,
@IntRange(from = 0, to = 100) final int value) {
// do nothing
}
/**
* Called when an {@link BluetoothGatt#GATT_INSUFFICIENT_AUTHENTICATION} error occurred and the
* device bond state is {@link BluetoothDevice#BOND_NONE}.
*
* @param device the device that requires bonding.
*/
void onBondingRequired(@NonNull final BluetoothDevice device);
/**
* Called when the device has been successfully bonded.
*
* @param device the device that got bonded.
*/
void onBonded(@NonNull final BluetoothDevice device);
/**
* Called when the bond state has changed from {@link BluetoothDevice#BOND_BONDING} to
* {@link BluetoothDevice#BOND_NONE}.
*
* @param device the device that failed to bond.
*/
void onBondingFailed(@NonNull final BluetoothDevice device);
/**
* Called when a BLE error has occurred
*
* @param message the error message.
* @param errorCode the error code.
* @param device the device that caused an error.
*/
void onError(@NonNull final BluetoothDevice device,
@NonNull final String message, final int errorCode);
/**
* Called when service discovery has finished but the main services were not found on the device.
*
* @param device the device that failed to connect due to lack of required services.
*/
void onDeviceNotSupported(@NonNull final BluetoothDevice device);
}

View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.content.Context;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.annotation.PhyMask;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
/**
* The connect request is used to connect to a Bluetooth LE device. The request will end when
* the device gets connected, the connection timeouts, or an error occurs.
* <p>
* The {@link #done(SuccessCallback)} callback will be called after the device is ready, that is
* when it is connected, the services were discovered, the required services were found and the
* initialization queue set in {@link BleManager.BleManagerGattCallback#initialize()} is complete
* (without or with errors).
*/
@SuppressWarnings({"WeakerAccess", "unused", "deprecation"})
public class ConnectRequest extends TimeoutableRequest {
@NonNull
private BluetoothDevice device;
@PhyMask
private int preferredPhy;
@IntRange(from = 0)
private int attempt = 0, retries = 0;
@IntRange(from = 0)
private int delay = 0;
private boolean autoConnect = false;
ConnectRequest(@NonNull final Type type, @NonNull final BluetoothDevice device) {
super(type);
this.device = device;
this.preferredPhy = PhyRequest.PHY_LE_1M_MASK;
}
@NonNull
@Override
ConnectRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@NonNull
@Override
public ConnectRequest timeout(@IntRange(from = 0) final long timeout) {
super.timeout(timeout);
return this;
}
/**
* Use to set a completion callback. The callback will be invoked when the operation has
* finished successfully unless {@link #await()} or its variant was used, in which case this
* callback will be ignored.
* <p>
* The done callback will also be called when one or more of initialization requests has
* failed due to a reason other than disconnect event. This is because
* {@link BleManagerCallbacks#onDeviceReady(BluetoothDevice)} is called no matter
* if the requests succeeded, or not. Set failure callbacks to initialization requests
* to get information about failures.
*
* @param callback the callback.
* @return The request.
*/
@NonNull
@Override
public ConnectRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@NonNull
@Override
public ConnectRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public ConnectRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public ConnectRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
/**
* Sets an optional retry count. The BleManager will do that many attempts to connect to the
* device in case of an error. The library will NOT retry if the device is not reachable,
* that is when the 30 sec. timeout occurs. In that case the app should scan before
* connecting, to make sure the target is in range.
*
* @param count how many times should the BleManager retry to connect.
* @return The request.
* @see #retry(int, int)
*/
public ConnectRequest retry(@IntRange(from = 0) final int count) {
this.retries = count;
this.delay = 0;
return this;
}
/**
* Sets an optional retry count and a delay that the process will wait before each connection
* attempt. The library will NOT retry if the device is not reachable, that is when the 30 sec.
* timeout occurs. In that case the app should scan before connecting, to make sure the
* target is in range.
*
* @param count how many times should the BleManager retry to connect.
* @param delay the delay between each connection attempt, in milliseconds.
* The real delay will be 200 ms longer than specified, as
* {@link BluetoothGatt#clone()} is estimated to last
* {@link BleManager#internalConnect(BluetoothDevice, ConnectRequest) 200 ms}.
* @return The request.
* @see #retry(int)
*/
public ConnectRequest retry(@IntRange(from = 0) final int count,
@IntRange(from = 0) final int delay) {
this.retries = count;
this.delay = delay;
return this;
}
/**
* This method replaces the {@link BleManager#shouldAutoConnect()} method.
* <p>
* Sets whether to connect to the remote device just once (false) or to add the address to
* white list of devices that will be automatically connect as soon as they become available
* (true). In the latter case, if Bluetooth adapter is enabled, Android scans periodically
* for devices from the white list and, if an advertising packet is received from such, it tries
* to connect to it. When the connection is lost, the system will keep trying to reconnect to
* it. If method is called with parameter set to true, and the connection to the device is
* lost, the {@link BleManagerCallbacks#onLinkLossOccurred(BluetoothDevice)} callback is
* called instead of {@link BleManagerCallbacks#onDeviceDisconnected(BluetoothDevice)}.
* <p>
* This feature works much better on newer Android phone models and may have issues on older
* phones.
* <p>
* This method should only be used with bonded devices, as otherwise the device may change
* it's address. It will however work also with non-bonded devices with private static address.
* A connection attempt to a non-bonded device with private resolvable address will fail.
* <p>
* The first connection to a device will always be created with autoConnect flag to false
* (see {@link BluetoothDevice#connectGatt(Context, boolean, BluetoothGattCallback)}). This is
* to make it quick as the user most probably waits for a quick response. If autoConnect is
* used (true), the following connections will be done using {@link BluetoothGatt#connect()},
* which forces the autoConnect parameter to true.
*
* @param autoConnect true to use autoConnect feature on the second and following connections.
* The first connection is always done with autoConnect parameter equal to
* false, to make it faster and allow to timeout it the device is unreachable.
* Default value is false.
* @return The request.
*/
public ConnectRequest useAutoConnect(final boolean autoConnect) {
this.autoConnect = autoConnect;
return this;
}
/**
* Sets the preferred PHY used for connection. Th value should be a bitmask composed of
* {@link PhyRequest#PHY_LE_1M_MASK}, {@link PhyRequest#PHY_LE_2M_MASK} or
* {@link PhyRequest#PHY_LE_CODED_MASK}.
* <p>
* Different PHYs are available only on more recent devices with Android 8+.
* Check {@link BluetoothAdapter#isLe2MPhySupported()} and
* {@link BluetoothAdapter#isLeCodedPhySupported()} if required PHYs are supported by this
* Android device. The default PHY is {@link PhyRequest#PHY_LE_1M_MASK}.
*
* @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of
* {@link PhyRequest#PHY_LE_1M_MASK}, {@link PhyRequest#PHY_LE_2M_MASK},
* and {@link PhyRequest#PHY_LE_CODED_MASK}. This option does not take effect
* if {@code autoConnect} is set to true.
* @return The request.
*/
public ConnectRequest usePreferredPhy(@PhyMask final int phy) {
this.preferredPhy = phy;
return this;
}
@NonNull
public BluetoothDevice getDevice() {
return device;
}
@PhyMask
int getPreferredPhy() {
return preferredPhy;
}
boolean canRetry() {
if (retries > 0) {
retries -= 1;
return true;
}
return false;
}
boolean isFirstAttempt() {
return attempt++ == 0;
}
@IntRange(from = 0)
int getRetryDelay() {
return delay;
}
boolean shouldAutoConnect() {
return autoConnect;
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import no.nordicsemi.android.ble.annotation.ConnectionPriority;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.ConnectionPriorityCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class ConnectionPriorityRequest extends SimpleValueRequest<ConnectionPriorityCallback>
implements Operation {
/**
* Connection parameter update - Use the connection parameters recommended by the
* Bluetooth SIG. This is the default value if no connection parameter update
* is requested.
* <p>
* Interval: 30 - 50 ms, latency: 0, supervision timeout: 20 sec.
*/
public static final int CONNECTION_PRIORITY_BALANCED = 0;
/**
* Connection parameter update - Request a high priority, low latency connection.
* An application should only request high priority connection parameters to transfer
* large amounts of data over LE quickly. Once the transfer is complete, the application
* should request {@link #CONNECTION_PRIORITY_BALANCED} connection parameters
* to reduce energy use.
* <p>
* Interval: 11.25 - 15 ms (Android 6+) or 7.5 - 10 ms (Android 4.3 - 5.1),
* latency: 0, supervision timeout: 20 sec.
*/
public static final int CONNECTION_PRIORITY_HIGH = 1;
/**
* Connection parameter update - Request low power, reduced data rate connection parameters.
* <p>
* Interval: 100 - 125 ms, latency: 2, supervision timeout: 20 sec.
*/
public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
private final int value;
ConnectionPriorityRequest(@NonNull final Type type, @ConnectionPriority int priority) {
super(type);
if (priority < 0 || priority > 2)
priority = CONNECTION_PRIORITY_BALANCED;
this.value = priority;
}
@NonNull
@Override
ConnectionPriorityRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public ConnectionPriorityRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public ConnectionPriorityRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public ConnectionPriorityRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public ConnectionPriorityRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@RequiresApi(value = Build.VERSION_CODES.O)
@Override
@NonNull
public ConnectionPriorityRequest with(@NonNull final ConnectionPriorityCallback callback) {
// The BluetoothGattCallback#onConnectionUpdated callback was introduced in Android Oreo.
super.with(callback);
return this;
}
@RequiresApi(value = Build.VERSION_CODES.O)
@NonNull
@Override
public <E extends ConnectionPriorityCallback> E await(@NonNull final Class<E> responseClass)
throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException {
// The BluetoothGattCallback#onConnectionUpdated callback was introduced in Android Oreo.
return super.await(responseClass);
}
@RequiresApi(value = Build.VERSION_CODES.O)
@NonNull
@Override
public <E extends ConnectionPriorityCallback> E await(@NonNull final E response)
throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException {
// The BluetoothGattCallback#onConnectionUpdated callback was introduced in Android Oreo.
return super.await(response);
}
@RequiresApi(api = Build.VERSION_CODES.O)
void notifyConnectionPriorityChanged(@NonNull final BluetoothDevice device,
@IntRange(from = 6, to = 3200) final int interval,
@IntRange(from = 0, to = 499) final int latency,
@IntRange(from = 10, to = 3200) final int timeout) {
if (valueCallback != null)
valueCallback.onConnectionUpdated(device, interval, latency, timeout);
}
@ConnectionPriority
int getRequiredPriority() {
return value;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
@SuppressWarnings({"WeakerAccess", "unused", "deprecation"})
public class DisconnectRequest extends TimeoutableRequest {
DisconnectRequest(@NonNull final Type type) {
super(type);
}
@NonNull
@Override
DisconnectRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@NonNull
@Override
public DisconnectRequest timeout(@IntRange(from = 0) final long timeout) {
super.timeout(timeout);
return this;
}
@NonNull
@Override
public DisconnectRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@NonNull
@Override
public DisconnectRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public DisconnectRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public DisconnectRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.IntRange;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import no.nordicsemi.android.ble.annotation.ConnectionState;
import no.nordicsemi.android.ble.annotation.PhyValue;
/**
* This class ensures that the BLE callbacks will be called on the main (UI) thread.
* Handler parameter was added to {@link android.bluetooth.BluetoothDevice
* #connectGatt(Context, boolean, BluetoothGattCallback, int, int, Handler)}
* in Android Oreo, before that the behavior was undefined.
*/
abstract class MainThreadBluetoothGattCallback extends BluetoothGattCallback {
private Handler mHandler;
void setHandler(@NonNull final Handler handler) {
mHandler = handler;
}
private void runOnUiThread(@NonNull final Runnable runnable) {
if (Looper.myLooper() != Looper.getMainLooper()) {
mHandler.post(runnable);
} else {
runnable.run();
}
}
abstract void onConnectionStateChangeSafe(@NonNull final BluetoothGatt gatt, final int status,
final int newState);
abstract void onServicesDiscoveredSafe(@NonNull final BluetoothGatt gatt, final int status);
abstract void onCharacteristicReadSafe(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] data,
final int status);
abstract void onCharacteristicWriteSafe(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] data,
final int status);
abstract void onCharacteristicChangedSafe(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] data);
abstract void onDescriptorReadSafe(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattDescriptor descriptor,
@Nullable final byte[] data,
final int status);
abstract void onDescriptorWriteSafe(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattDescriptor descriptor,
@Nullable final byte[] data,
final int status);
abstract void onReadRemoteRssiSafe(@NonNull final BluetoothGatt gatt,
@IntRange(from = -128, to = 20) final int rssi,
final int status);
abstract void onReliableWriteCompletedSafe(@NonNull final BluetoothGatt gatt, final int status);
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
abstract void onMtuChangedSafe(@NonNull final BluetoothGatt gatt,
@IntRange(from = 23, to = 517) final int mtu, final int status);
@RequiresApi(api = Build.VERSION_CODES.O)
abstract void onPhyReadSafe(@NonNull final BluetoothGatt gatt,
@PhyValue final int txPhy, @PhyValue final int rxPhy, final int status);
@RequiresApi(api = Build.VERSION_CODES.O)
abstract void onPhyUpdateSafe(@NonNull final BluetoothGatt gatt,
@PhyValue final int txPhy, @PhyValue final int rxPhy, final int status);
@RequiresApi(api = Build.VERSION_CODES.O)
abstract void onConnectionUpdatedSafe(@NonNull final BluetoothGatt gatt,
@IntRange(from = 6, to = 3200) final int interval,
@IntRange(from = 0, to = 499) final int latency,
@IntRange(from = 10, to = 3200) final int timeout,
final int status);
@Override
public final void onConnectionStateChange(@NonNull final BluetoothGatt gatt, final int status,
@ConnectionState final int newState) {
runOnUiThread(() -> onConnectionStateChangeSafe(gatt, status, newState));
}
@Override
public final void onServicesDiscovered(@NonNull final BluetoothGatt gatt, final int status) {
runOnUiThread(() -> onServicesDiscoveredSafe(gatt, status));
}
@Override
public final void onCharacteristicRead(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattCharacteristic characteristic,
final int status) {
final byte[] data = characteristic.getValue();
runOnUiThread(() -> onCharacteristicReadSafe(gatt, characteristic, data, status));
}
@Override
public final void onCharacteristicWrite(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattCharacteristic characteristic,
final int status) {
final byte[] data = characteristic.getValue();
runOnUiThread(() -> onCharacteristicWriteSafe(gatt, characteristic, data, status));
}
@Override
public final void onCharacteristicChanged(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattCharacteristic characteristic) {
final byte[] data = characteristic.getValue();
runOnUiThread(() -> onCharacteristicChangedSafe(gatt, characteristic, data));
}
@Override
public final void onDescriptorRead(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattDescriptor descriptor,
final int status) {
final byte[] data = descriptor.getValue();
runOnUiThread(() -> onDescriptorReadSafe(gatt, descriptor, data, status));
}
@Override
public final void onDescriptorWrite(@NonNull final BluetoothGatt gatt,
@NonNull final BluetoothGattDescriptor descriptor,
final int status) {
final byte[] data = descriptor.getValue();
runOnUiThread(() -> onDescriptorWriteSafe(gatt, descriptor, data, status));
}
@Override
public final void onReadRemoteRssi(@NonNull final BluetoothGatt gatt,
@IntRange(from = -128, to = 20) final int rssi,
final int status) {
runOnUiThread(() -> onReadRemoteRssiSafe(gatt, rssi, status));
}
@Override
public final void onReliableWriteCompleted(@NonNull final BluetoothGatt gatt, final int status) {
runOnUiThread(() -> onReliableWriteCompletedSafe(gatt, status));
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public final void onMtuChanged(@NonNull final BluetoothGatt gatt,
@IntRange(from = 23, to = 517) final int mtu, final int status) {
runOnUiThread(() -> onMtuChangedSafe(gatt, mtu, status));
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public final void onPhyRead(@NonNull final BluetoothGatt gatt,
@PhyValue final int txPhy, @PhyValue final int rxPhy,
final int status) {
runOnUiThread(() -> onPhyReadSafe(gatt, txPhy, rxPhy, status));
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public final void onPhyUpdate(@NonNull final BluetoothGatt gatt,
@PhyValue final int txPhy, @PhyValue final int rxPhy,
final int status) {
runOnUiThread(() -> onPhyUpdateSafe(gatt, txPhy, rxPhy, status));
}
// This method is hidden in Android Oreo and Pie
// @Override
@SuppressWarnings("unused")
@RequiresApi(api = Build.VERSION_CODES.O)
@Keep
public final void onConnectionUpdated(@NonNull final BluetoothGatt gatt,
@IntRange(from = 6, to = 3200) final int interval,
@IntRange(from = 0, to = 499) final int latency,
@IntRange(from = 10, to = 3200) final int timeout,
final int status) {
runOnUiThread(() -> onConnectionUpdatedSafe(gatt, interval, latency, timeout, status));
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.MtuCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
public final class MtuRequest extends SimpleValueRequest<MtuCallback>implements Operation {
private final int value;
MtuRequest(@NonNull final Type type, @IntRange(from = 23, to = 517) int mtu) {
super(type);
if (mtu < 23)
mtu = 23;
if (mtu > 517)
mtu = 517;
this.value = mtu;
}
@NonNull
@Override
MtuRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public MtuRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public MtuRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public MtuRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public MtuRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@Override
@NonNull
public MtuRequest with(@NonNull final MtuCallback callback) {
super.with(callback);
return this;
}
void notifyMtuChanged(@NonNull final BluetoothDevice device,
@IntRange(from = 23, to = 517) final int mtu) {
if (valueCallback != null)
valueCallback.onMtuChanged(device, mtu);
}
int getRequiredMtu() {
return value;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
/**
* An operation is a request that can be executed during a pending connection without
* changing connection state. Such requests may be added to {@link RequestQueue}.
*/
@SuppressWarnings("WeakerAccess")
public interface Operation {
// empty
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.annotation.PhyMask;
import no.nordicsemi.android.ble.annotation.PhyOption;
import no.nordicsemi.android.ble.annotation.PhyValue;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.PhyCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
@SuppressWarnings({"WeakerAccess", "unused"})
public final class PhyRequest extends SimpleValueRequest<PhyCallback> implements Operation {
/**
* Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available
* options in a bitmask.
*/
public static final int PHY_LE_1M_MASK = 1;
/**
* Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available
* options in a bitmask.
*/
public static final int PHY_LE_2M_MASK = 2;
/**
* Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many
* available options in a bitmask.
*/
public static final int PHY_LE_CODED_MASK = 4;
/**
* No preferred coding when transmitting on the LE Coded PHY.
*/
public static final int PHY_OPTION_NO_PREFERRED = 0;
/**
* Prefer the S=2 coding to be used when transmitting on the LE Coded PHY.
*/
public static final int PHY_OPTION_S2 = 1;
/**
* Prefer the S=8 coding to be used when transmitting on the LE Coded PHY.
*/
public static final int PHY_OPTION_S8 = 2;
private final int txPhy;
private final int rxPhy;
private final int phyOptions;
PhyRequest(@NonNull final Type type) {
super(type);
this.txPhy = 0;
this.rxPhy = 0;
this.phyOptions = 0;
}
PhyRequest(@NonNull final Type type,
@PhyMask int txPhy, @PhyMask int rxPhy, @PhyOption int phyOptions) {
super(type);
if ((txPhy & ~(PHY_LE_1M_MASK | PHY_LE_2M_MASK | PHY_LE_CODED_MASK)) > 0)
txPhy = PHY_LE_1M_MASK;
if ((rxPhy & ~(PHY_LE_1M_MASK | PHY_LE_2M_MASK | PHY_LE_CODED_MASK)) > 0)
rxPhy = PHY_LE_1M_MASK;
if (phyOptions < PHY_OPTION_NO_PREFERRED || phyOptions > PHY_OPTION_S8)
phyOptions = PHY_OPTION_NO_PREFERRED;
this.txPhy = txPhy;
this.rxPhy = rxPhy;
this.phyOptions = phyOptions;
}
@NonNull
@Override
PhyRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public PhyRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public PhyRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public PhyRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public PhyRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@Override
@NonNull
public PhyRequest with(@NonNull final PhyCallback callback) {
super.with(callback);
return this;
}
void notifyPhyChanged(@NonNull final BluetoothDevice device,
@PhyValue final int txPhy, @PhyValue final int rxPhy) {
if (valueCallback != null)
valueCallback.onPhyChanged(device, txPhy, rxPhy);
}
void notifyLegacyPhy(@NonNull final BluetoothDevice device) {
if (valueCallback != null)
valueCallback.onPhyChanged(device, PhyCallback.PHY_LE_1M, PhyCallback.PHY_LE_1M);
}
@PhyMask
int getPreferredTxPhy() {
return txPhy;
}
@PhyMask
int getPreferredRxPhy() {
return rxPhy;
}
@PhyOption
int getPreferredPhyOptions() {
return phyOptions;
}
}

View File

@@ -0,0 +1,249 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.DataReceivedCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.ReadProgressCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.callback.profile.ProfileReadResponse;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataFilter;
import no.nordicsemi.android.ble.data.DataMerger;
import no.nordicsemi.android.ble.data.DataStream;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidDataException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class ReadRequest extends SimpleValueRequest<DataReceivedCallback> implements Operation {
private ReadProgressCallback progressCallback;
private DataMerger dataMerger;
private DataStream buffer;
private DataFilter filter;
private int count = 0;
ReadRequest(@NonNull final Type type) {
super(type);
}
ReadRequest(@NonNull final Type type, @Nullable final BluetoothGattCharacteristic characteristic) {
super(type, characteristic);
}
ReadRequest(@NonNull final Type type, @Nullable final BluetoothGattDescriptor descriptor) {
super(type, descriptor);
}
@NonNull
@Override
ReadRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public ReadRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public ReadRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public ReadRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public ReadRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@Override
@NonNull
public ReadRequest with(@NonNull final DataReceivedCallback callback) {
super.with(callback);
return this;
}
/**
* Sets a filter which allows to skip some incoming data.
*
* @param filter the data filter.
* @return The request.
*/
@NonNull
public ReadRequest filter(@NonNull final DataFilter filter) {
this.filter = filter;
return this;
}
/**
* Adds a merger that will be used to merge multiple packets into a single Data.
* The merger may modify each packet if necessary.
*
* @return The request.
*/
@NonNull
public ReadRequest merge(@NonNull final DataMerger merger) {
this.dataMerger = merger;
this.progressCallback = null;
return this;
}
/**
* Adds a merger that will be used to merge multiple packets into a single Data.
* The merger may modify each packet if necessary.
*
* @return The request.
*/
@NonNull
public ReadRequest merge(@NonNull final DataMerger merger,
@NonNull final ReadProgressCallback callback) {
this.dataMerger = merger;
this.progressCallback = callback;
return this;
}
/**
* Same as {@link #await(Class)}, but if the response class extends
* {@link ProfileReadResponse} and the received response is not valid
* ({@link ProfileReadResponse#isValid()} returns false), this method will
* throw an exception.
*
* @param responseClass the response class. This class will be instantiate, therefore it
* has to have a default constructor.
* @return The object with the response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link BluetoothGatt#GATT_SUCCESS}.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws IllegalArgumentException thrown when the response class could not be instantiated.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidDataException thrown when the received data were not valid (that is when
* {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)}
* failed to parse the data correctly and called
* {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}).
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
*/
@NonNull
public <E extends ProfileReadResponse> E awaitValid(@NonNull final Class<E> responseClass)
throws RequestFailedException, InvalidDataException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException {
final E response = await(responseClass);
if (!response.isValid()) {
throw new InvalidDataException(response);
}
return response;
}
/**
* Same as {@link #await(Object)}, but if the response class extends
* {@link ProfileReadResponse} and the received response is not valid
* ({@link ProfileReadResponse#isValid()} returns false), this method will
* throw an exception.
*
* @param response the response object.
* @return The object with the response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link BluetoothGatt#GATT_SUCCESS}.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidDataException thrown when the received data were not valid (that is when
* {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)}
* failed to parse the data correctly and called
* {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}).
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
*/
@NonNull
public <E extends ProfileReadResponse> E awaitValid(@NonNull final E response)
throws RequestFailedException, InvalidDataException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException {
await(response);
if (!response.isValid()) {
throw new InvalidDataException(response);
}
return response;
}
boolean matches(final byte[] packet) {
return filter == null || filter.filter(packet);
}
void notifyValueChanged(@NonNull final BluetoothDevice device, @Nullable final byte[] value) {
// Keep a reference to the value callback, as it may change during execution
final DataReceivedCallback valueCallback = this.valueCallback;
// With no value callback there is no need for any merging
if (valueCallback == null)
return;
if (dataMerger == null) {
valueCallback.onDataReceived(device, new Data(value));
} else {
if (progressCallback != null)
progressCallback.onPacketReceived(device, value, count);
if (buffer == null)
buffer = new DataStream();
if (dataMerger.merge(buffer, value, count++)) {
valueCallback.onDataReceived(device, buffer.toData());
buffer = null;
count = 0;
} // else
// wait for more packets to be merged
}
}
boolean hasMore() {
return count > 0;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.RssiCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
public final class ReadRssiRequest extends SimpleValueRequest<RssiCallback> implements Operation {
ReadRssiRequest(@NonNull final Type type) {
super(type);
}
@NonNull
@Override
ReadRssiRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public ReadRssiRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public ReadRssiRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public ReadRssiRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public ReadRssiRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@Override
@NonNull
public ReadRssiRequest with(@NonNull final RssiCallback callback) {
super.with(callback);
return this;
}
void notifyRssiRead(@NonNull final BluetoothDevice device,
@IntRange(from = -128, to = 20) final int rssi) {
if (valueCallback != null)
valueCallback.onRssiRead(device, rssi);
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
@SuppressWarnings("unused")
public final class ReliableWriteRequest extends RequestQueue {
private boolean initialized;
private boolean closed;
private boolean cancelled;
@NonNull
@Override
ReliableWriteRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public ReliableWriteRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public ReliableWriteRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public ReliableWriteRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public ReliableWriteRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@NonNull
@Override
public ReliableWriteRequest add(@NonNull final Operation operation) {
super.add(operation);
// Make sure the write request uses splitting, as Long Write is not supported
// in Reliable Write sub-procedure.
if (operation instanceof WriteRequest) {
((WriteRequest) operation).forceSplit();
}
return this;
}
@Override
public void cancelQueue() {
cancelled = true;
super.cancelQueue();
}
/**
* Alias for {@link #cancelQueue()}.
*/
public void abort() {
cancelQueue();
}
@Override
public int size() {
int size = super.size();
// Add Begin Reliable Write
if (!initialized)
size += 1;
// Add Execute or Abort Reliable Write
if (!closed)
size += 1;
return size;
}
@Override
Request getNext() {
if (!initialized) {
initialized = true;
return newBeginReliableWriteRequest();
}
if (super.isEmpty()) {
closed = true;
if (cancelled)
return newAbortReliableWriteRequest();
return newExecuteReliableWriteRequest();
}
return super.getNext();
}
@Override
boolean hasMore() {
// If no operations were added, consider the RW request empty, no requests will be executed.
if (!initialized)
return super.hasMore();
return !closed;
}
}

View File

@@ -0,0 +1,896 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.os.ConditionVariable;
import android.os.Looper;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.annotation.ConnectionPriority;
import no.nordicsemi.android.ble.annotation.PhyMask;
import no.nordicsemi.android.ble.annotation.PhyOption;
import no.nordicsemi.android.ble.annotation.WriteType;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.data.Data;
/**
* On Android, when multiple BLE operations needs to be done, it is required to wait for a proper
* {@link BluetoothGattCallback} callback before calling another operation.
* In order to make BLE operations easier the BleManager allows to enqueue a request containing all
* data necessary for a given operation. Requests are performed one after another until the queue
* is empty.
*/
@SuppressWarnings({"unused", "WeakerAccess", "deprecation", "DeprecatedIsStillUsed"})
public abstract class Request {
enum Type {
SET,
CONNECT,
DISCONNECT,
CREATE_BOND,
REMOVE_BOND,
WRITE,
READ,
WRITE_DESCRIPTOR,
READ_DESCRIPTOR,
BEGIN_RELIABLE_WRITE,
EXECUTE_RELIABLE_WRITE,
ABORT_RELIABLE_WRITE,
ENABLE_NOTIFICATIONS,
ENABLE_INDICATIONS,
DISABLE_NOTIFICATIONS,
DISABLE_INDICATIONS,
WAIT_FOR_NOTIFICATION,
WAIT_FOR_INDICATION,
@Deprecated
READ_BATTERY_LEVEL,
@Deprecated
ENABLE_BATTERY_LEVEL_NOTIFICATIONS,
@Deprecated
DISABLE_BATTERY_LEVEL_NOTIFICATIONS,
ENABLE_SERVICE_CHANGED_INDICATIONS,
REQUEST_MTU,
REQUEST_CONNECTION_PRIORITY,
SET_PREFERRED_PHY,
READ_PHY,
READ_RSSI,
REFRESH_CACHE,
SLEEP,
}
private BleManager manager;
final ConditionVariable syncLock;
final Type type;
final BluetoothGattCharacteristic characteristic;
final BluetoothGattDescriptor descriptor;
BeforeCallback beforeCallback;
SuccessCallback successCallback;
FailCallback failCallback;
InvalidRequestCallback invalidRequestCallback;
BeforeCallback internalBeforeCallback;
SuccessCallback internalSuccessCallback;
FailCallback internalFailCallback;
boolean enqueued;
boolean finished;
Request(@NonNull final Type type) {
this.type = type;
this.characteristic = null;
this.descriptor = null;
this.syncLock = new ConditionVariable(true);
}
Request(@NonNull final Type type, @Nullable final BluetoothGattCharacteristic characteristic) {
this.type = type;
this.characteristic = characteristic;
this.descriptor = null;
this.syncLock = new ConditionVariable(true);
}
Request(@NonNull final Type type, @Nullable final BluetoothGattDescriptor descriptor) {
this.type = type;
this.characteristic = null;
this.descriptor = descriptor;
this.syncLock = new ConditionVariable(true);
}
/**
* Sets the {@link BleManager} instance.
*
* @param manager the manager in which the request will be executed.
*/
@NonNull
Request setManager(@NonNull final BleManager manager) {
this.manager = manager;
return this;
}
/**
* Creates a new connect request. This allows to set a callback to the connect event,
* just like any other request.
*
* @param device the device to connect to.
* @return The new connect request.
*/
@NonNull
static ConnectRequest connect(@NonNull final BluetoothDevice device) {
return new ConnectRequest(Type.CONNECT, device);
}
/**
* Creates a new disconnect request. This allows to set a callback to a disconnect event,
* just like any other request.
*
* @return The new disconnect request.
*/
@NonNull
static DisconnectRequest disconnect() {
return new DisconnectRequest(Type.DISCONNECT);
}
/**
* Creates a new request that will start pairing with the device.
*
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#createBond()} instead.
*/
@Deprecated
@NonNull
public static SimpleRequest createBond() {
return new SimpleRequest(Type.CREATE_BOND);
}
/**
* Creates a new request that will remove the bonding information from the Android device.
* This is done using reflections and may not work on all devices.
* <p>
* The device will disconnect after calling this method. The success callback will be called
* after the device got disconnected, when the {@link BluetoothDevice#getBondState()} changes
* to {@link BluetoothDevice#BOND_NONE}.
*
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#removeBond()} instead.
*/
@Deprecated
@NonNull
public static SimpleRequest removeBond() {
return new SimpleRequest(Type.REMOVE_BOND);
}
/**
* Creates new Read Characteristic request. The request will not be executed if given
* characteristic is null or does not have READ property.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to be read.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#readCharacteristic(BluetoothGattCharacteristic)} instead.
*/
@Deprecated
@NonNull
public static ReadRequest newReadRequest(
@Nullable final BluetoothGattCharacteristic characteristic) {
return new ReadRequest(Type.READ, characteristic);
}
/**
* Creates new Write Characteristic request. The request will not be executed if given
* characteristic is null or does not have WRITE property.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to be written.
* @param value value to be written. The array is copied into another buffer so it's
* safe to reuse the array again.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#writeCharacteristic(BluetoothGattCharacteristic, byte[])} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newWriteRequest(
@Nullable final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] value) {
return new WriteRequest(Type.WRITE, characteristic, value, 0,
value != null ? value.length : 0,
characteristic != null ?
characteristic.getWriteType() :
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
}
/**
* Creates new Write Characteristic request. The request will not be executed if given
* characteristic is null or does not have WRITE property.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to be written.
* @param value value to be written. The array is copied into another buffer so it's
* safe to reuse the array again.
* @param writeType write type to be used, one of
* {@link BluetoothGattCharacteristic#WRITE_TYPE_DEFAULT},
* {@link BluetoothGattCharacteristic#WRITE_TYPE_NO_RESPONSE}.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#writeCharacteristic(BluetoothGattCharacteristic, Data)} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newWriteRequest(
@Nullable final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] value, @WriteType final int writeType) {
return new WriteRequest(Type.WRITE, characteristic, value, 0,
value != null ? value.length : 0, writeType);
}
/**
* Creates new Write Characteristic request. The request will not be executed if given
* characteristic is null or does not have WRITE property.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to be written.
* @param value value to be written. The array is copied into another buffer so it's
* safe to reuse the array again.
* @param offset the offset from which value has to be copied.
* @param length number of bytes to be copied from the value buffer.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#writeCharacteristic(BluetoothGattCharacteristic, byte[], int, int)}
* instead.
*/
@Deprecated
@NonNull
public static WriteRequest newWriteRequest(
@Nullable final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] value,
@IntRange(from = 0) final int offset, @IntRange(from = 0) final int length) {
return new WriteRequest(Type.WRITE, characteristic, value, offset, length,
characteristic != null ?
characteristic.getWriteType() :
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
}
/**
* Creates new Write Characteristic request. The request will not be executed if given
* characteristic is null or does not have WRITE property.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to be written.
* @param value value to be written. The array is copied into another buffer so it's
* safe to reuse the array again.
* @param offset the offset from which value has to be copied.
* @param length number of bytes to be copied from the value buffer.
* @param writeType write type to be used, one of
* {@link BluetoothGattCharacteristic#WRITE_TYPE_DEFAULT},
* {@link BluetoothGattCharacteristic#WRITE_TYPE_NO_RESPONSE} or
* {@link BluetoothGattCharacteristic#WRITE_TYPE_SIGNED}.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#writeCharacteristic(BluetoothGattCharacteristic, byte[], int, int)}
* instead.
*/
@Deprecated
@NonNull
public static WriteRequest newWriteRequest(
@Nullable final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] value,
@IntRange(from = 0) final int offset, @IntRange(from = 0) final int length,
@WriteType final int writeType) {
return new WriteRequest(Type.WRITE, characteristic, value, offset, length, writeType);
}
/**
* Creates new Read Descriptor request. The request will not be executed if given descriptor
* is null. After the operation is complete a proper callback will be invoked.
*
* @param descriptor descriptor to be read.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#readDescriptor(BluetoothGattDescriptor)} instead.
*/
@Deprecated
@NonNull
public static ReadRequest newReadRequest(@Nullable final BluetoothGattDescriptor descriptor) {
return new ReadRequest(Type.READ_DESCRIPTOR, descriptor);
}
/**
* Creates new Write Descriptor request. The request will not be executed if given descriptor
* is null. After the operation is complete a proper callback will be invoked.
*
* @param descriptor descriptor to be written.
* @param value value to be written. The array is copied into another buffer so it's safe
* to reuse the array again.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#writeDescriptor(BluetoothGattDescriptor, byte[])} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newWriteRequest(@Nullable final BluetoothGattDescriptor descriptor,
@Nullable final byte[] value) {
return new WriteRequest(Type.WRITE_DESCRIPTOR, descriptor, value, 0,
value != null ? value.length : 0);
}
/**
* Creates new Write Descriptor request. The request will not be executed if given descriptor
* is null. After the operation is complete a proper callback will be invoked.
*
* @param descriptor descriptor to be written.
* @param value value to be written. The array is copied into another buffer so it's safe
* to reuse the array again.
* @param offset the offset from which value has to be copied.
* @param length number of bytes to be copied from the value buffer.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#writeDescriptor(BluetoothGattDescriptor, byte[], int, int)} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newWriteRequest(
@Nullable final BluetoothGattDescriptor descriptor,
@Nullable final byte[] value,
@IntRange(from = 0) final int offset, @IntRange(from = 0) final int length) {
return new WriteRequest(Type.WRITE_DESCRIPTOR, descriptor, value, offset, length);
}
/**
* Creates new Reliable Write request. All operations that need to be executed
* reliably should be enqueued inside the returned request before enqueuing it in the
* BleManager. The library will automatically verify the data sent
*
* @return The new request.
*/
@NonNull
static ReliableWriteRequest newReliableWriteRequest() {
return new ReliableWriteRequest();
}
/**
* Creates new Begin Reliable Write request.
*
* @return The new request.
*/
@NonNull
static SimpleRequest newBeginReliableWriteRequest() {
return new SimpleRequest(Type.BEGIN_RELIABLE_WRITE);
}
/**
* Executes Reliable Write sub-procedure. At lease one Write Request must be performed
* before the Reliable Write is to be executed, otherwise error
* {@link no.nordicsemi.android.ble.error.GattError#GATT_INVALID_OFFSET} will be returned.
*
* @return The new request.
*/
@NonNull
static SimpleRequest newExecuteReliableWriteRequest() {
return new SimpleRequest(Type.EXECUTE_RELIABLE_WRITE);
}
/**
* Aborts Reliable Write sub-procedure. All write requests performed during Reliable Write will
* be cancelled. At lease one Write Request must be performed before the Reliable Write
* is to be executed, otherwise error
* {@link no.nordicsemi.android.ble.error.GattError#GATT_INVALID_OFFSET} will be returned.
*
* @return The new request.
*/
@NonNull
static SimpleRequest newAbortReliableWriteRequest() {
return new SimpleRequest(Type.ABORT_RELIABLE_WRITE);
}
/**
* Creates new Enable Notification request. The request will not be executed if given
* characteristic is null, does not have NOTIFY property or the CCCD.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to have notifications enabled.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#enableNotifications(BluetoothGattCharacteristic)} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newEnableNotificationsRequest(
@Nullable final BluetoothGattCharacteristic characteristic) {
return new WriteRequest(Type.ENABLE_NOTIFICATIONS, characteristic);
}
/**
* Creates new Disable Notification request. The request will not be executed if given
* characteristic is null, does not have NOTIFY property or the CCCD.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to have notifications disabled.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#disableNotifications(BluetoothGattCharacteristic)} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newDisableNotificationsRequest(
@Nullable final BluetoothGattCharacteristic characteristic) {
return new WriteRequest(Type.DISABLE_NOTIFICATIONS, characteristic);
}
/**
* Creates new Enable Indications request. The request will not be executed if given
* characteristic is null, does not have INDICATE property or the CCCD.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to have indications enabled.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#enableIndications(BluetoothGattCharacteristic)} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newEnableIndicationsRequest(
@Nullable final BluetoothGattCharacteristic characteristic) {
return new WriteRequest(Type.ENABLE_INDICATIONS, characteristic);
}
/**
* Creates new Disable Indications request. The request will not be executed if given
* characteristic is null, does not have INDICATE property or the CCCD.
* After the operation is complete a proper callback will be invoked.
*
* @param characteristic characteristic to have indications disabled.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#disableNotifications(BluetoothGattCharacteristic)} instead.
*/
@Deprecated
@NonNull
public static WriteRequest newDisableIndicationsRequest(
@Nullable final BluetoothGattCharacteristic characteristic) {
return new WriteRequest(Type.DISABLE_INDICATIONS, characteristic);
}
/**
* Creates new Wait For Notification request. The request will not be executed if given
* characteristic is null, does not have NOTIFY property or the CCCD.
* After the operation is complete a proper callback will be invoked.
* <p>
* If the notification should be triggered by another operation (for example writing an
* op code), set it with {@link WaitForValueChangedRequest#trigger(Operation)}.
*
* @param characteristic characteristic from which a notification should be received.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#waitForNotification(BluetoothGattCharacteristic)} instead.
*/
@Deprecated
@NonNull
public static WaitForValueChangedRequest newWaitForNotificationRequest(
@Nullable final BluetoothGattCharacteristic characteristic) {
return new WaitForValueChangedRequest(Type.WAIT_FOR_NOTIFICATION, characteristic);
}
/**
* Creates new Wait For Indication request. The request will not be executed if given
* characteristic is null, does not have INDICATE property or the CCCD.
* After the operation is complete a proper callback will be invoked.
* <p>
* If the indication should be triggered by another operation (for example writing an
* op code), set it with {@link WaitForValueChangedRequest#trigger(Operation)}.
*
* @param characteristic characteristic from which a notification should be received.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#waitForIndication(BluetoothGattCharacteristic)} instead.
*/
@Deprecated
@NonNull
public static WaitForValueChangedRequest newWaitForIndicationRequest(
@Nullable final BluetoothGattCharacteristic characteristic) {
return new WaitForValueChangedRequest(Type.WAIT_FOR_INDICATION, characteristic);
}
/**
* Creates new Read Battery Level request. The first found Battery Level characteristic value
* from the first found Battery Service. If any of them is not found, or the characteristic
* does not have the READ property this operation will not execute.
*
* @return The new request.
* @deprecated Use {@link #newReadRequest(BluetoothGattCharacteristic)} with
* BatteryLevelDataCallback from Android BLE Common Library instead.
*/
@NonNull
@Deprecated
public static ReadRequest newReadBatteryLevelRequest() {
return new ReadRequest(Type.READ_BATTERY_LEVEL);
}
/**
* Creates new Enable Notifications on the first found Battery Level characteristic from the
* first found Battery Service. If any of them is not found, or the characteristic does not
* have the NOTIFY property this operation will not execute.
*
* @return The new request.
* @deprecated Use {@link #newEnableNotificationsRequest(BluetoothGattCharacteristic)} with
* BatteryLevelDataCallback from Android BLE Common Library instead.
*/
@NonNull
@Deprecated
public static WriteRequest newEnableBatteryLevelNotificationsRequest() {
return new WriteRequest(Type.ENABLE_BATTERY_LEVEL_NOTIFICATIONS);
}
/**
* Creates new Disable Notifications on the first found Battery Level characteristic from the
* first found Battery Service. If any of them is not found, or the characteristic does not
* have the NOTIFY property this operation will not execute.
*
* @return The new request.
* @deprecated Use {@link #newDisableNotificationsRequest(BluetoothGattCharacteristic)} instead.
*/
@NonNull
@Deprecated
public static WriteRequest newDisableBatteryLevelNotificationsRequest() {
return new WriteRequest(Type.DISABLE_BATTERY_LEVEL_NOTIFICATIONS);
}
/**
* Creates new Enable Indications on Service Changed characteristic. It is a NOOP if such
* characteristic does not exist in the Generic Attribute service.
* It is required to enable those notifications on bonded devices on older Android versions to
* be informed about attributes changes.
* Android 7+ (or 6+) handles this automatically and no action is required.
*
* @return The new request.
*/
@NonNull
static WriteRequest newEnableServiceChangedIndicationsRequest() {
return new WriteRequest(Type.ENABLE_SERVICE_CHANGED_INDICATIONS);
}
/**
* Requests new MTU (Maximum Transfer Unit). This is only supported on Android Lollipop or newer.
* On older platforms the request will enqueue, but will fail to execute and
* {@link #fail(FailCallback)} callback will be called.
* The target device may reject requested value and set a smaller MTU.
*
* @param mtu the new MTU. Acceptable values are &lt;23, 517&gt;.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#requestMtu(int)} instead.
*/
@Deprecated
@NonNull
public static MtuRequest newMtuRequest(@IntRange(from = 23, to = 517) final int mtu) {
return new MtuRequest(Type.REQUEST_MTU, mtu);
}
/**
* Requests the new connection priority. Acceptable values are:
* <ol>
* <li>{@link ConnectionPriorityRequest#CONNECTION_PRIORITY_HIGH}
* - Interval: 11.25 -15 ms (Android 6+) and 7.5 - 10 ms (older), latency: 0,
* supervision timeout: 20 sec,</li>
* <li>{@link ConnectionPriorityRequest#CONNECTION_PRIORITY_BALANCED}
* - Interval: 30 - 50 ms, latency: 0, supervision timeout: 20 sec,</li>
* <li>{@link ConnectionPriorityRequest#CONNECTION_PRIORITY_LOW_POWER}
* - Interval: 100 - 125 ms, latency: 2, supervision timeout: 20 sec.</li>
* </ol>
* Requesting connection priority is available on Android Lollipop or newer. On older
* platforms the request will enqueue, but will fail to execute and {@link #fail(FailCallback)}
* callback will be called.
*
* @param priority one of: {@link ConnectionPriorityRequest#CONNECTION_PRIORITY_HIGH},
* {@link ConnectionPriorityRequest#CONNECTION_PRIORITY_BALANCED},
* {@link ConnectionPriorityRequest#CONNECTION_PRIORITY_LOW_POWER}.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#requestConnectionPriority(int)} instead.
*/
@Deprecated
@NonNull
public static ConnectionPriorityRequest newConnectionPriorityRequest(
@ConnectionPriority final int priority) {
return new ConnectionPriorityRequest(Type.REQUEST_CONNECTION_PRIORITY, priority);
}
/**
* Requests the change of preferred PHY for this connections.
* <p>
* PHY LE 2M and PHY LE Coded are supported only on Android Oreo or newer.
* You may safely request other PHYs on older platforms, but the request will not be executed
* and you will get PHY LE 1M as TX and RX PHY in the callback.
*
* @param txPhy preferred transmitter PHY. Bitwise OR of any of
* {@link PhyRequest#PHY_LE_1M_MASK}, {@link PhyRequest#PHY_LE_2M_MASK},
* and {@link PhyRequest#PHY_LE_CODED_MASK}.
* @param rxPhy preferred receiver PHY. Bitwise OR of any of
* {@link PhyRequest#PHY_LE_1M_MASK}, {@link PhyRequest#PHY_LE_2M_MASK},
* and {@link PhyRequest#PHY_LE_CODED_MASK}.
* @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
* of {@link PhyRequest#PHY_OPTION_NO_PREFERRED},
* {@link PhyRequest#PHY_OPTION_S2} or {@link PhyRequest#PHY_OPTION_S8}.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#setPreferredPhy(int, int, int)} instead.
*/
@Deprecated
@NonNull
public static PhyRequest newSetPreferredPhyRequest(@PhyMask final int txPhy,
@PhyMask final int rxPhy,
@PhyOption final int phyOptions) {
return new PhyRequest(Type.SET_PREFERRED_PHY, txPhy, rxPhy, phyOptions);
}
/**
* Reads the current PHY for this connections.
* <p>
* PHY LE 2M and PHY LE Coded are supported only on Android Oreo or newer.
* You may safely read PHY on older platforms, but the request will not be executed
* and you will get PHY LE 1M as TX and RX PHY in the callback.
*
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#readPhy()} instead.
*/
@Deprecated
@NonNull
public static PhyRequest newReadPhyRequest() {
return new PhyRequest(Type.READ_PHY);
}
/**
* Reads the current RSSI (Received Signal Strength Indication).
*
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#readRssi()} instead.
*/
@Deprecated
@NonNull
public static ReadRssiRequest newReadRssiRequest() {
return new ReadRssiRequest(Type.READ_RSSI);
}
/**
* Refreshes the device cache. As the {@link BluetoothGatt#refresh()} method is not in the
* public API (it's hidden, and on Android P it is on a light gray list) it is called
* using reflections and may be removed in some future Android release or on some devices.
* <p>
* There is no callback indicating when the cache has been cleared. This library assumes
* some time and waits. After the delay, it will start service discovery and clear the
* task queue. When the service discovery finishes, the
* {@link BleManager.BleManagerGattCallback#isRequiredServiceSupported(BluetoothGatt)} and
* {@link BleManager.BleManagerGattCallback#isOptionalServiceSupported(BluetoothGatt)} will
* be called and the initialization will be performed as if the device has just connected.
*
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#refreshDeviceCache()} instead.
*/
@Deprecated
@SuppressWarnings("JavadocReference")
@NonNull
public static SimpleRequest newRefreshCacheRequest() {
return new SimpleRequest(Type.REFRESH_CACHE);
}
/**
* Creates new Sleep request that will postpone next request for given number of milliseconds.
*
* @param delay the delay in milliseconds.
* @return The new request.
* @deprecated Access to this method will change to package-only.
* Use {@link BleManager#sleep(long)} instead.
*/
@Deprecated
@NonNull
public static SleepRequest newSleepRequest(@IntRange(from = 0) final long delay) {
return new SleepRequest(Type.SLEEP, delay);
}
/**
* Use to set a completion callback. The callback will be invoked when the operation has
* finished successfully unless the request was executed synchronously, in which case this
* callback will be ignored.
*
* @param callback the callback.
* @return The request.
*/
@NonNull
public Request done(@NonNull final SuccessCallback callback) {
this.successCallback = callback;
return this;
}
/**
* Use to set a callback that will be called in case the request has failed.
* If the target device wasn't set before executing this request
* ({@link BleManager#connect(BluetoothDevice)} was never called), the
* {@link #invalid(InvalidRequestCallback)} will be used instead, as the
* {@link BluetoothDevice} is not known.
* <p>
* This callback will be ignored if request was executed synchronously, in which case
* the error will be returned as an exception.
*
* @param callback the callback.
* @return The request.
*/
@NonNull
public Request fail(@NonNull final FailCallback callback) {
this.failCallback = callback;
return this;
}
/**
* Used to set internal callback what will be executed before the request is executed.
*
* @param callback the callback.
*/
void internalBefore(@NonNull final BeforeCallback callback) {
this.internalBeforeCallback = callback;
}
/**
* Used to set internal success callback. The callback will be notified in case the request
* has completed.
*
* @param callback the callback.
*/
void internalSuccess(@NonNull final SuccessCallback callback) {
this.internalSuccessCallback = callback;
}
/**
* Used to set internal fail callback. The callback will be notified in case the request
* has failed.
*
* @param callback the callback.
*/
void internalFail(@NonNull final FailCallback callback) {
this.internalFailCallback = callback;
}
/**
* Use to set a callback that will be called in case the request was invalid, for example
* called before the device was connected.
* This callback will be ignored if request was executed synchronously, in which case
* the error will be returned as an exception.
*
* @param callback the callback.
* @return The request.
*/
@NonNull
public Request invalid(@NonNull final InvalidRequestCallback callback) {
this.invalidRequestCallback = callback;
return this;
}
/**
* Sets a callback that will be executed before the execution of this operation starts.
*
* @param callback the callback.
* @return The request.
*/
@NonNull
public Request before(@NonNull final BeforeCallback callback) {
this.beforeCallback = callback;
return this;
}
/**
* Enqueues the request for asynchronous execution.
*/
public void enqueue() {
manager.enqueue(this);
}
void notifyStarted(@NonNull final BluetoothDevice device) {
if (beforeCallback != null)
beforeCallback.onRequestStarted(device);
if (internalBeforeCallback != null)
internalBeforeCallback.onRequestStarted(device);
}
void notifySuccess(@NonNull final BluetoothDevice device) {
if (!finished) {
finished = true;
if (successCallback != null)
successCallback.onRequestCompleted(device);
if (internalSuccessCallback != null)
internalSuccessCallback.onRequestCompleted(device);
}
}
void notifyFail(@NonNull final BluetoothDevice device, final int status) {
if (!finished) {
finished = true;
if (failCallback != null)
failCallback.onRequestFailed(device, status);
if (internalFailCallback != null)
internalFailCallback.onRequestFailed(device, status);
}
}
void notifyInvalidRequest() {
if (!finished) {
finished = true;
if (invalidRequestCallback != null)
invalidRequestCallback.onInvalidRequest();
}
}
/**
* Asserts that the synchronous method was not called from the UI thread.
*
* @throws IllegalStateException when called from a UI thread.
*/
static void assertNotMainThread() throws IllegalStateException {
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new IllegalStateException("Cannot execute synchronous operation from the UI thread.");
}
}
final class RequestCallback implements SuccessCallback, FailCallback, InvalidRequestCallback {
final static int REASON_REQUEST_INVALID = -1000000;
int status = BluetoothGatt.GATT_SUCCESS;
@Override
public void onRequestCompleted(@NonNull final BluetoothDevice device) {
syncLock.open();
}
@Override
public void onRequestFailed(@NonNull final BluetoothDevice device, final int status) {
this.status = status;
syncLock.open();
}
@Override
public void onInvalidRequest() {
this.status = REASON_REQUEST_INVALID;
syncLock.open();
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
boolean isSuccess() {
return this.status == BluetoothGatt.GATT_SUCCESS;
}
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import java.util.LinkedList;
import java.util.Queue;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
@SuppressWarnings("WeakerAccess")
public class RequestQueue extends SimpleRequest {
/**
* A list of operations that will be executed together.
*/
@NonNull
private final Queue<Request> requests;
RequestQueue() {
super(Type.SET);
requests = new LinkedList<>();
}
@NonNull
@Override
RequestQueue setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public RequestQueue done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public RequestQueue fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public RequestQueue invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public RequestQueue before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
/**
* Enqueues a new operation. All operations will be executed sequentially in order they were
* added.
*
* @param operation the new operation to be enqueued.
* @throws IllegalStateException if the operation was enqueued before.
* @throws IllegalArgumentException if the operation is not a {@link Request}.
*/
@NonNull
public RequestQueue add(@NonNull final Operation operation) {
if (operation instanceof Request) {
final Request request = (Request) operation;
// Validate
if (request.enqueued)
throw new IllegalStateException("Request already enqueued");
// Add
requests.add(request);
// Mark
request.enqueued = true;
return this;
} else {
throw new IllegalArgumentException("Operation does not extend Request");
}
}
/**
* Returns number of enqueued operations.
*
* @return the size of the internal operations list.
*/
@IntRange(from = 0)
public int size() {
return requests.size();
}
/**
* Returns whether the set is empty, or not.
*
* @return true if the set is empty. Set gets emptied while it all enqueued operations
* are being executed.
*/
public boolean isEmpty() {
return requests.isEmpty();
}
/**
* Cancels all the enqueued operations that were not executed yet.
* The one currently executed will be finished.
* <p>
* It is safe to call this method in {@link Request#done(SuccessCallback)} or
* {@link Request#fail(FailCallback)} callback;
*/
public void cancelQueue() {
requests.clear();
}
/**
* Returns the next {@link Request} to be enqueued.
*
* @return the next request.
*/
@Nullable
Request getNext() {
try {
return requests.remove();
// poll() may also throw an exception
// See: https://github.com/NordicSemiconductor/Android-BLE-Library/issues/37
} catch (final Exception e) {
return null;
}
}
/**
* Returns whether there are more operations to be executed.
*
* @return true, if not all operations were completed.
*/
boolean hasMore() {
return !requests.isEmpty();
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;
/**
* A request that requires a {@link android.bluetooth.BluetoothGattCallback callback} or can't
* have timeout for any other reason. This class defines the {@link #await()} method.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class SimpleRequest extends Request {
SimpleRequest(@NonNull final Type type) {
super(type);
}
SimpleRequest(@NonNull final Type type,
@Nullable final BluetoothGattCharacteristic characteristic) {
super(type, characteristic);
}
SimpleRequest(@NonNull final Type type,
@Nullable final BluetoothGattDescriptor descriptor) {
super(type, descriptor);
}
/**
* Synchronously waits until the request is done.
* <p>
* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)}
* will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link BluetoothGatt#GATT_SUCCESS}.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
*/
public final void await() throws RequestFailedException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException {
assertNotMainThread();
final SuccessCallback sc = successCallback;
final FailCallback fc = failCallback;
try {
syncLock.close();
final RequestCallback callback = new RequestCallback();
done(callback).fail(callback).invalid(callback).enqueue();
syncLock.block();
if (!callback.isSuccess()) {
if (callback.status == FailCallback.REASON_DEVICE_DISCONNECTED) {
throw new DeviceDisconnectedException();
}
if (callback.status == FailCallback.REASON_BLUETOOTH_DISABLED) {
throw new BluetoothDisabledException();
}
if (callback.status == RequestCallback.REASON_REQUEST_INVALID) {
throw new InvalidRequestException(this);
}
throw new RequestFailedException(this, callback.status);
}
} finally {
successCallback = sc;
failCallback = fc;
}
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;
/**
* A value request that requires a {@link android.bluetooth.BluetoothGattCallback callback} or
* can't have timeout for any other reason. This class defines the {@link #await()} methods.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class SimpleValueRequest<T> extends SimpleRequest {
T valueCallback;
SimpleValueRequest(@NonNull final Type type) {
super(type);
}
SimpleValueRequest(@NonNull final Type type,
@Nullable final BluetoothGattCharacteristic characteristic) {
super(type, characteristic);
}
SimpleValueRequest(@NonNull final Type type,
@Nullable final BluetoothGattDescriptor descriptor) {
super(type, descriptor);
}
/**
* Sets the value callback. When the request is invoked synchronously, this callback will
* be ignored and the received value will be returned by the <code>await(...)</code> method;
*
* @param callback the callback.
* @return The request.
*/
@NonNull
public SimpleValueRequest<T> with(@NonNull final T callback) {
this.valueCallback = callback;
return this;
}
/**
* Synchronously waits until the request is done. The given response object will be filled
* with the request response.
* <p>
* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)} and
* {@link #with(T)} will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @param response the response object.
* @param <E> a response class.
* @return The response with a response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @see #await(Class)
*/
@NonNull
public <E extends T> E await(@NonNull final E response)
throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException {
assertNotMainThread();
final T vc = valueCallback;
try {
with(response).await();
return response;
} finally {
valueCallback = vc;
}
}
/**
* Synchronously waits until the request is done.
* <p>
* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)} and
* {@link #with(T)} will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @param responseClass the response class. This class will be instantiate, therefore it has
* to have a default constructor.
* @return The response with a response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws IllegalArgumentException thrown when the response class could not be instantiated.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @see #await(Object)
*/
@NonNull
public <E extends T> E await(@NonNull final Class<E> responseClass)
throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException {
assertNotMainThread();
try {
final E response = responseClass.newInstance();
return await(response);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Couldn't instantiate "
+ responseClass.getCanonicalName()
+ " class. Is the default constructor accessible?");
} catch (InstantiationException e) {
throw new IllegalArgumentException("Couldn't instantiate "
+ responseClass.getCanonicalName()
+ " class. Does it have a default constructor with no arguments?");
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class SleepRequest extends SimpleRequest implements Operation {
private long delay;
SleepRequest(@NonNull final Type type, @IntRange(from = 0) final long delay) {
super(type);
this.delay = delay;
}
@NonNull
@Override
SleepRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@NonNull
@Override
public SleepRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@NonNull
@Override
public SleepRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public SleepRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@NonNull
@Override
public SleepRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
long getDelay() {
return delay;
}
}

View File

@@ -0,0 +1,13 @@
package no.nordicsemi.android.ble;
import androidx.annotation.NonNull;
abstract class TimeoutHandler {
/**
* Method called when the request timed out.
*
* @param request the request that timed out.
*/
abstract void onRequestTimeout(@NonNull final TimeoutableRequest request);
}

View File

@@ -0,0 +1,222 @@
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.os.Handler;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;
@SuppressWarnings("WeakerAccess")
public abstract class TimeoutableRequest extends Request {
private TimeoutHandler timeoutHandler;
private Runnable timeoutCallback;
private Handler handler;
protected long timeout;
TimeoutableRequest(@NonNull final Type type) {
super(type);
}
TimeoutableRequest(@NonNull final Type type, @Nullable final BluetoothGattCharacteristic characteristic) {
super(type, characteristic);
}
TimeoutableRequest(@NonNull final Type type, @Nullable final BluetoothGattDescriptor descriptor) {
super(type, descriptor);
}
@NonNull
@Override
TimeoutableRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
this.handler = manager.mHandler;
this.timeoutHandler = manager;
return this;
}
/**
* Sets the operation timeout.
* When the timeout occurs, the request will fail with {@link FailCallback#REASON_TIMEOUT}.
*
* @param timeout the request timeout in milliseconds, 0 to disable timeout.
* @return the callback.
* @throws IllegalStateException thrown when the request has already been started.
* @throws UnsupportedOperationException thrown when the timeout is not allowed for this request,
* as the callback from the system is required.
*/
@NonNull
public TimeoutableRequest timeout(@IntRange(from = 0) final long timeout) {
if (timeoutCallback != null)
throw new IllegalStateException("Request already started");
this.timeout = timeout;
return this;
}
/**
* Enqueues the request for asynchronous execution.
* <p>
* Use {@link #timeout(long)} to set the maximum time the manager should wait until the device
* is ready. When the timeout occurs, the request will fail with
* {@link FailCallback#REASON_TIMEOUT} and the device will get disconnected.
*/
@Override
public final void enqueue() {
super.enqueue();
}
/**
* Enqueues the request for asynchronous execution.
* <p>
* When the timeout occurs, the request will fail with {@link FailCallback#REASON_TIMEOUT}
* and the device will get disconnected.
*
* @param timeout the request timeout in milliseconds, 0 to disable timeout. This value will
* override one set in {@link #timeout(long)}.
* @deprecated Use {@link #timeout(long)} and {@link #enqueue()} instead.
*/
@Deprecated
public final void enqueue(@IntRange(from = 0) final long timeout) {
timeout(timeout).enqueue();
}
/**
* Synchronously waits until the request is done.
* <p>
* Use {@link #timeout(long)} to set the maximum time the manager should wait until the request
* is ready. When the timeout occurs, the {@link InterruptedException} will be thrown.
* <p>
* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)}
* will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link BluetoothGatt#GATT_SUCCESS}.
* @throws InterruptedException thrown if the timeout occurred before the request has
* finished.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @see #enqueue()
*/
public final void await() throws RequestFailedException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException, InterruptedException {
assertNotMainThread();
final SuccessCallback sc = successCallback;
final FailCallback fc = failCallback;
try {
syncLock.close();
final RequestCallback callback = new RequestCallback();
done(callback).fail(callback).invalid(callback).enqueue();
if (!syncLock.block(timeout)) {
throw new InterruptedException();
}
if (!callback.isSuccess()) {
if (callback.status == FailCallback.REASON_DEVICE_DISCONNECTED) {
throw new DeviceDisconnectedException();
}
if (callback.status == FailCallback.REASON_BLUETOOTH_DISABLED) {
throw new BluetoothDisabledException();
}
if (callback.status == RequestCallback.REASON_REQUEST_INVALID) {
throw new InvalidRequestException(this);
}
throw new RequestFailedException(this, callback.status);
}
} finally {
successCallback = sc;
failCallback = fc;
}
}
/**
* Synchronously waits, for as most as the given number of milliseconds, until the request
* is ready.
* <p>
* When the timeout occurs, the {@link InterruptedException} will be thrown.
* <p>
* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)}
* will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @param timeout optional timeout in milliseconds, 0 to disable timeout. This will
* override the timeout set using {@link #timeout(long)}.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link BluetoothGatt#GATT_SUCCESS}.
* @throws InterruptedException thrown if the timeout occurred before the request has
* finished.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @deprecated Use {@link #timeout(long)} and {@link #await()} instead.
*/
@Deprecated
public final void await(@IntRange(from = 0) final long timeout) throws RequestFailedException,
InterruptedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException {
timeout(timeout).await();
}
@Override
void notifyStarted(@NonNull final BluetoothDevice device) {
if (timeout > 0L) {
timeoutCallback = () -> {
timeoutCallback = null;
if (!finished) {
notifyFail(device, FailCallback.REASON_TIMEOUT);
timeoutHandler.onRequestTimeout(this);
}
};
handler.postDelayed(timeoutCallback, timeout);
}
super.notifyStarted(device);
}
@Override
void notifySuccess(@NonNull final BluetoothDevice device) {
if (!finished) {
handler.removeCallbacks(timeoutCallback);
timeoutCallback = null;
}
super.notifySuccess(device);
}
@Override
void notifyFail(@NonNull final BluetoothDevice device, final int status) {
if (!finished) {
handler.removeCallbacks(timeoutCallback);
timeoutCallback = null;
}
super.notifyFail(device, status);
}
@Override
void notifyInvalidRequest() {
if (!finished) {
handler.removeCallbacks(timeoutCallback);
timeoutCallback = null;
}
super.notifyInvalidRequest();
}
}

View File

@@ -0,0 +1,238 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;
/**
* A value request that requires a {@link android.bluetooth.BluetoothGattCallback callback} or
* can't have timeout for any other reason. This class defines the {@link #await()} methods.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class TimeoutableValueRequest<T> extends TimeoutableRequest {
T valueCallback;
TimeoutableValueRequest(@NonNull final Type type) {
super(type);
}
TimeoutableValueRequest(@NonNull final Type type,
@Nullable final BluetoothGattCharacteristic characteristic) {
super(type, characteristic);
}
TimeoutableValueRequest(@NonNull final Type type,
@Nullable final BluetoothGattDescriptor descriptor) {
super(type, descriptor);
}
@NonNull
@Override
public TimeoutableValueRequest<T> timeout(@IntRange(from = 0) final long timeout) {
super.timeout(timeout);
return this;
}
/**
* Sets the value callback. When the request is invoked synchronously, this callback will
* be ignored and the received value will be returned by the <code>await(...)</code> method;
*
* @param callback the callback.
* @return The request.
*/
@NonNull
public TimeoutableValueRequest<T> with(@NonNull final T callback) {
this.valueCallback = callback;
return this;
}
/**
* Synchronously waits until the request is done.
* <p>
* When the timeout, set with {@link #timeout(long)} occurs, the {@link InterruptedException}
* will be thrown.
* <p>
* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)} and
* {@link #with(E)} will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @param response the response object.
* @param <E> a response class.
* @return The response with a response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
* @throws InterruptedException thrown if the timeout occurred before the request has
* finished.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread, or when the trigger was already enqueued.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
*/
@NonNull
public <E extends T> E await(@NonNull final E response)
throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException, InterruptedException {
assertNotMainThread();
final T vc = valueCallback;
try {
with(response).await();
return response;
} finally {
valueCallback = vc;
}
}
/**
* Synchronously waits until the request is done.
* <p>
* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)} and
* {@link #with(T)} will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @param responseClass the response class. This class will be instantiate, therefore it has
* to have a default constructor.
* @return The response with a response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
* @throws InterruptedException thrown if the timeout occurred before the request has
* finished.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws IllegalArgumentException thrown when the response class could not be instantiated.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @see #await(Object)
*/
@NonNull
public <E extends T> E await(@NonNull final Class<E> responseClass)
throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException, InterruptedException {
assertNotMainThread();
try {
final E response = responseClass.newInstance();
return await(response);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Couldn't instantiate "
+ responseClass.getCanonicalName()
+ " class. Is the default constructor accessible?");
} catch (InstantiationException e) {
throw new IllegalArgumentException("Couldn't instantiate "
+ responseClass.getCanonicalName()
+ " class. Does it have a default constructor with no arguments?");
}
}
/**
* Synchronously waits until the request is done, for at most given number of milliseconds
* after which the {@link InterruptedException} will be thrown.
* <p>
* Callbacks set using {@link #done(SuccessCallback)}, {@link #fail(FailCallback)} and
* {@link #with(E)} will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @param responseClass the response class. This class will be instantiate, therefore it has
* to have a default constructor.
* @param timeout optional timeout in milliseconds. This value will override one set
* in {@link #timeout(long)}.
* @param <E> a response class that extends {@link T}.
* @return The object with a response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
* @throws InterruptedException thrown if the timeout occurred before the request has
* finished.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws IllegalArgumentException thrown when the response class could not be instantiated.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @deprecated Use {@link #timeout(long)} and {@link #await(Class)} instead.
*/
@NonNull
@Deprecated
public <E extends T> E await(@NonNull final Class<E> responseClass,
@IntRange(from = 0) final long timeout)
throws RequestFailedException, InterruptedException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException {
return timeout(timeout).await(responseClass);
}
/**
* Synchronously waits until the request is done, for at most given number of milliseconds
* after which the {@link InterruptedException} will be thrown.
* <p>
* Callbacks set using {@link #done(SuccessCallback)}, {@link #fail(FailCallback)} and
* {@link #with(E)} will be ignored.
* <p>
* This method may not be called from the main (UI) thread.
*
* @param response the response object.
* @param timeout optional timeout in milliseconds.
* @param <E> a response class that extends {@link T}.
* @return The object with a response.
* @throws RequestFailedException thrown when the BLE request finished with status other
* than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
* @throws InterruptedException thrown if the timeout occurred before the request has
* finished.
* @throws IllegalStateException thrown when you try to call this method from the main
* (UI) thread.
* @throws DeviceDisconnectedException thrown when the device disconnected before the request
* was completed.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @deprecated Use {@link #timeout(long)} and {@link #await(E)} instead.
*/
@NonNull
@Deprecated
public <E extends T> E await(@NonNull final E response,
@IntRange(from = 0) final long timeout)
throws RequestFailedException, InterruptedException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException {
return timeout(timeout).await(response);
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.DataReceivedCallback;
import no.nordicsemi.android.ble.callback.ReadProgressCallback;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataFilter;
import no.nordicsemi.android.ble.data.DataMerger;
import no.nordicsemi.android.ble.data.DataStream;
@SuppressWarnings({"unused", "WeakerAccess", "UnusedReturnValue"})
public class ValueChangedCallback {
private ReadProgressCallback progressCallback;
private DataReceivedCallback valueCallback;
private DataMerger dataMerger;
private DataStream buffer;
private DataFilter filter;
private int count = 0;
ValueChangedCallback() {
// empty
}
/**
* Sets the asynchronous data callback that will be called whenever a notification or
* an indication is received on given characteristic.
*
* @param callback the data callback.
* @return The request.
*/
@NonNull
public ValueChangedCallback with(@NonNull final DataReceivedCallback callback) {
this.valueCallback = callback;
return this;
}
/**
* Sets a filter which allows to skip some incoming data.
*
* @param filter the data filter.
* @return The request.
*/
@NonNull
public ValueChangedCallback filter(@NonNull final DataFilter filter) {
this.filter = filter;
return this;
}
/**
* Adds a merger that will be used to merge multiple packets into a single Data.
* The merger may modify each packet if necessary.
*
* @return The request.
*/
@NonNull
public ValueChangedCallback merge(@NonNull final DataMerger merger) {
this.dataMerger = merger;
this.progressCallback = null;
return this;
}
/**
* Adds a merger that will be used to merge multiple packets into a single Data.
* The merger may modify each packet if necessary.
*
* @return The request.
*/
@NonNull
public ValueChangedCallback merge(@NonNull final DataMerger merger,
@NonNull final ReadProgressCallback callback) {
this.dataMerger = merger;
this.progressCallback = callback;
return this;
}
ValueChangedCallback free() {
valueCallback = null;
dataMerger = null;
progressCallback = null;
buffer = null;
return this;
}
boolean matches(final byte[] packet) {
return filter == null || filter.filter(packet);
}
void notifyValueChanged(@NonNull final BluetoothDevice device, @Nullable final byte[] value) {
// Keep a reference to the value callback, as it may change during execution
final DataReceivedCallback valueCallback = this.valueCallback;
// With no value callback there is no need for any merging
if (valueCallback == null) {
return;
}
if (dataMerger == null) {
valueCallback.onDataReceived(device, new Data(value));
} else {
if (progressCallback != null)
progressCallback.onPacketReceived(device, value, count);
if (buffer == null)
buffer = new DataStream();
if (dataMerger.merge(buffer, value, count++)) {
valueCallback.onDataReceived(device, buffer.toData());
buffer = null;
count = 0;
} // else
// wait for more packets to be merged
}
}
}

View File

@@ -0,0 +1,400 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.DataReceivedCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.ReadProgressCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.callback.profile.ProfileReadResponse;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataFilter;
import no.nordicsemi.android.ble.data.DataMerger;
import no.nordicsemi.android.ble.data.DataStream;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidDataException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class WaitForValueChangedRequest extends TimeoutableValueRequest<DataReceivedCallback>
implements Operation {
static final int NOT_STARTED = -123456;
static final int STARTED = NOT_STARTED + 1;
private ReadProgressCallback progressCallback;
private DataMerger dataMerger;
private DataStream buffer;
private DataFilter filter;
private Request trigger;
private boolean deviceDisconnected;
private boolean bluetoothDisabled;
private int triggerStatus = BluetoothGatt.GATT_SUCCESS;
private int count = 0;
WaitForValueChangedRequest(@NonNull final Type type,
@Nullable final BluetoothGattCharacteristic characteristic) {
super(type, characteristic);
}
@NonNull
@Override
WaitForValueChangedRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@NonNull
@Override
public WaitForValueChangedRequest timeout(@IntRange(from = 0) final long timeout) {
super.timeout(timeout);
return this;
}
@NonNull
@Override
public WaitForValueChangedRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@NonNull
@Override
public WaitForValueChangedRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public WaitForValueChangedRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public WaitForValueChangedRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@NonNull
@Override
public WaitForValueChangedRequest with(@NonNull final DataReceivedCallback callback) {
super.with(callback);
return this;
}
/**
* Sets a filter which allows to skip some incoming data.
*
* @param filter the data filter.
* @return The request.
*/
@NonNull
public WaitForValueChangedRequest filter(@NonNull final DataFilter filter) {
this.filter = filter;
return this;
}
/**
* Adds a merger that will be used to merge multiple packets into a single Data.
* The merger may modify each packet if necessary.
*
* @return The request.
*/
@NonNull
public WaitForValueChangedRequest merge(@NonNull final DataMerger merger) {
this.dataMerger = merger;
this.progressCallback = null;
return this;
}
/**
* Adds a merger that will be used to merge multiple packets into a single Data.
* The merger may modify each packet if necessary.
*
* @return The request.
*/
@NonNull
public WaitForValueChangedRequest merge(@NonNull final DataMerger merger,
@NonNull final ReadProgressCallback callback) {
this.dataMerger = merger;
this.progressCallback = callback;
return this;
}
/**
* Sets an optional request that is suppose to trigger the notification or indication.
* This is to ensure that the characteristic value won't change before the callback was set.
*
* @param trigger the operation that triggers the notification, usually a write characteristic
* request that write some OP CODE.
* @return The request.
*/
@NonNull
public WaitForValueChangedRequest trigger(@NonNull final Operation trigger) {
if (trigger instanceof Request) {
this.trigger = (Request) trigger;
this.triggerStatus = NOT_STARTED;
// The trigger will never receive invalid request event.
// If the BluetoothDevice wasn't set, the whole WaitForValueChangedRequest would be invalid.
/*this.trigger.invalid(() -> {
// never called
});*/
this.trigger.internalBefore(device -> triggerStatus = STARTED);
this.trigger.internalSuccess(device -> triggerStatus = BluetoothGatt.GATT_SUCCESS);
this.trigger.internalFail((device, status) -> {
triggerStatus = status;
syncLock.open();
notifyFail(device, status);
});
}
return this;
}
@NonNull
@Override
public <E extends DataReceivedCallback> E await(@NonNull final E response)
throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException,
InvalidRequestException, InterruptedException {
assertNotMainThread();
try {
// Ensure the trigger request it enqueued after the callback has been set.
if (trigger != null && trigger.enqueued) {
throw new IllegalStateException("Trigger request already enqueued");
}
super.await(response);
return response;
} catch (final RequestFailedException e) {
if (triggerStatus != BluetoothGatt.GATT_SUCCESS) {
// Trigger will never have invalid request status. The outer request will.
/*if (triggerStatus == RequestCallback.REASON_REQUEST_INVALID) {
throw new InvalidRequestException(trigger);
}*/
throw new RequestFailedException(trigger, triggerStatus);
}
throw e;
}
}
/**
* Similar to {@link #await(Class)}, but if the response class extends
* {@link ProfileReadResponse} and the received response is invalid, an exception is thrown.
* This allows to keep all error handling in one place.
*
* @param response the result object.
* @param <E> a response class that extends {@link ProfileReadResponse}.
* @return Object with a valid response.
* @throws IllegalStateException thrown when you try to call this method from
* the main (UI) thread.
* @throws InterruptedException thrown when the timeout occurred before the
* characteristic value has changed.
* @throws RequestFailedException thrown when the trigger request has failed.
* @throws DeviceDisconnectedException thrown when the device disconnected before the
* notification or indication was received.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @throws InvalidDataException thrown when the received data were not valid (that is when
* {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)}
* failed to parse the data correctly and called
* {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}).
*/
@SuppressWarnings("ConstantConditions")
@NonNull
public <E extends ProfileReadResponse> E awaitValid(@NonNull final E response)
throws RequestFailedException, InvalidDataException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException, InterruptedException {
final E result = await(response);
if (result != null && !result.isValid()) {
throw new InvalidDataException(result);
}
return result;
}
/**
* Similar to {@link #await(DataReceivedCallback)}, but if the response class extends
* {@link ProfileReadResponse} and the received response is invalid, an exception is thrown.
* This allows to keep all error handling in one place.
*
* @param responseClass the result class. This class will be instantiate, therefore it
* has to have a default constructor.
* @param <E> a response class that extends {@link ProfileReadResponse}.
* @return Object with a valid response.
* @throws IllegalStateException thrown when you try to call this method from
* the main (UI) thread.
* @throws InterruptedException thrown when the timeout occurred before the
* characteristic value has changed.
* @throws IllegalArgumentException thrown when the response class could not be instantiated.
* @throws RequestFailedException thrown when the trigger request has failed.
* @throws DeviceDisconnectedException thrown when the device disconnected before the
* notification or indication was received.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @throws InvalidDataException thrown when the received data were not valid (that is when
* {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)}
* failed to parse the data correctly and called
* {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}).
*/
@SuppressWarnings("ConstantConditions")
@NonNull
public <E extends ProfileReadResponse> E awaitValid(@NonNull final Class<E> responseClass)
throws RequestFailedException, InvalidDataException, DeviceDisconnectedException,
BluetoothDisabledException, InvalidRequestException, InterruptedException {
final E response = await(responseClass);
if (response != null && !response.isValid()) {
throw new InvalidDataException(response);
}
return response;
}
/**
* Same as {@link #await(Class, long)}, but if received response is not valid, this method will
* thrown an exception.
*
* @param responseClass the result class. This class will be instantiate, therefore it
* has to have a default constructor.
* @param timeout optional timeout in milliseconds.
* @param <E> a response class that extends {@link ProfileReadResponse}.
* @return Object with a valid response.
* @throws IllegalStateException thrown when you try to call this method from
* the main (UI) thread.
* @throws InterruptedException thrown when the timeout occurred before the
* characteristic value has changed.
* @throws IllegalArgumentException thrown when the response class could not be instantiated.
* @throws RequestFailedException thrown when the trigger request has failed.
* @throws DeviceDisconnectedException thrown when the device disconnected before the
* notification or indication was received.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @throws InvalidDataException thrown when the received data were not valid (that is when
* {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)}
* failed to parse the data correctly and called
* {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}).
* @deprecated Use {@link #timeout(long)} and {@link #awaitValid(Class)} instead.
*/
@SuppressWarnings("ConstantConditions")
@NonNull
@Deprecated
public <E extends ProfileReadResponse> E awaitValid(@NonNull final Class<E> responseClass,
@IntRange(from = 0) final long timeout)
throws InterruptedException, InvalidDataException, RequestFailedException,
DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException {
return timeout(timeout).awaitValid(responseClass);
}
/**
* Same as {@link #await(Object, long)}, but if received response is not valid,
* this method will thrown an exception.
*
* @param response the result object.
* @param timeout optional timeout in milliseconds.
* @param <E> a response class that extends {@link ProfileReadResponse}.
* @return Object with a valid response.
* @throws IllegalStateException thrown when you try to call this method from
* the main (UI) thread.
* @throws InterruptedException thrown when the timeout occurred before the
* characteristic value has changed.
* @throws RequestFailedException thrown when the trigger request has failed.
* @throws DeviceDisconnectedException thrown when the device disconnected before the
* notification or indication was received.
* @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled.
* @throws InvalidRequestException thrown when the request was called before the device was
* connected at least once (unknown device).
* @throws InvalidDataException thrown when the received data were not valid (that is when
* {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)}
* failed to parse the data correctly and called
* {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}).
* @deprecated Use {@link #timeout(long)} and {@link #awaitValid(E)} instead.
*/
@SuppressWarnings("ConstantConditions")
@NonNull
@Deprecated
public <E extends ProfileReadResponse> E awaitValid(@NonNull final E response,
@IntRange(from = 0) final long timeout)
throws InterruptedException, InvalidDataException, DeviceDisconnectedException,
RequestFailedException, BluetoothDisabledException, InvalidRequestException {
return timeout(timeout).awaitValid(response);
}
boolean matches(final byte[] packet) {
return filter == null || filter.filter(packet);
}
void notifyValueChanged(final BluetoothDevice device, final byte[] value) {
// Keep a reference to the value callback, as it may change during execution
final DataReceivedCallback valueCallback = this.valueCallback;
// With no value callback there is no need for any merging
if (valueCallback == null) {
return;
}
if (dataMerger == null) {
valueCallback.onDataReceived(device, new Data(value));
} else {
if (progressCallback != null)
progressCallback.onPacketReceived(device, value, count);
if (buffer == null)
buffer = new DataStream();
if (dataMerger.merge(buffer, value, count++)) {
valueCallback.onDataReceived(device, buffer.toData());
buffer = null;
count = 0;
} // else
// wait for more packets to be merged
}
}
boolean hasMore() {
return count > 0;
}
@Nullable
Request getTrigger() {
return trigger;
}
boolean isTriggerPending() {
return triggerStatus == NOT_STARTED;
}
boolean isTriggerCompleteOrNull() {
return triggerStatus != STARTED;
}
}

View File

@@ -0,0 +1,282 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import java.util.Arrays;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.annotation.WriteType;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.DataSentCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.callback.WriteProgressCallback;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataSplitter;
import no.nordicsemi.android.ble.data.DefaultMtuSplitter;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class WriteRequest extends SimpleValueRequest<DataSentCallback> implements Operation {
private final static DataSplitter MTU_SPLITTER = new DefaultMtuSplitter();
private WriteProgressCallback progressCallback;
private DataSplitter dataSplitter;
private final byte[] data;
private final int writeType;
private byte[] currentChunk;
private byte[] nextChunk;
private int count = 0;
private boolean complete = false;
WriteRequest(@NonNull final Type type) {
this(type, null);
}
WriteRequest(@NonNull final Type type, @Nullable final BluetoothGattCharacteristic characteristic) {
super(type, characteristic);
// not used:
this.data = null;
this.writeType = 0;
// getData(int) isn't called on enabling and disabling notifications/indications.
this.complete = true;
}
WriteRequest(@NonNull final Type type, @Nullable final BluetoothGattCharacteristic characteristic,
@Nullable final byte[] data,
@IntRange(from = 0) final int offset, @IntRange(from = 0) final int length,
@WriteType final int writeType) {
super(type, characteristic);
this.data = copy(data, offset, length);
this.writeType = writeType;
}
WriteRequest(@NonNull final Type type, @Nullable final BluetoothGattDescriptor descriptor,
@Nullable final byte[] data,
@IntRange(from = 0) final int offset, @IntRange(from = 0) final int length) {
super(type, descriptor);
this.data = copy(data, offset, length);
this.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
}
@NonNull
@Override
WriteRequest setManager(@NonNull final BleManager manager) {
super.setManager(manager);
return this;
}
@Override
@NonNull
public WriteRequest done(@NonNull final SuccessCallback callback) {
super.done(callback);
return this;
}
@Override
@NonNull
public WriteRequest fail(@NonNull final FailCallback callback) {
super.fail(callback);
return this;
}
@NonNull
@Override
public WriteRequest invalid(@NonNull final InvalidRequestCallback callback) {
super.invalid(callback);
return this;
}
@Override
@NonNull
public WriteRequest before(@NonNull final BeforeCallback callback) {
super.before(callback);
return this;
}
@Override
@NonNull
public WriteRequest with(@NonNull final DataSentCallback callback) {
super.with(callback);
return this;
}
/**
* Adds a splitter that will be used to cut given data into multiple packets.
* The splitter may modify each packet if necessary, i.e. add a flag indicating first packet,
* continuation or the last packet.
*
* @param splitter an implementation of a splitter.
* @return The request.
* @see #split()
*/
@NonNull
public WriteRequest split(@NonNull final DataSplitter splitter) {
this.dataSplitter = splitter;
this.progressCallback = null;
return this;
}
/**
* Adds a splitter that will be used to cut given data into multiple packets.
* The splitter may modify each packet if necessary, i.e. add a flag indicating first packet,
* continuation or the last packet.
*
* @param splitter an implementation of a splitter.
* @param callback the progress callback that will be notified each time a packet was sent.
* @return The request.
* @see #split()
*/
@NonNull
public WriteRequest split(@NonNull final DataSplitter splitter,
@NonNull final WriteProgressCallback callback) {
this.dataSplitter = splitter;
this.progressCallback = callback;
return this;
}
/**
* Adds a default MTU splitter that will be used to cut given data into at-most MTU-3
* bytes long packets.
*
* @return The request.
*/
@NonNull
public WriteRequest split() {
this.dataSplitter = MTU_SPLITTER;
this.progressCallback = null;
return this;
}
/**
* Adds a default MTU splitter that will be used to cut given data into at-most MTU-3
* bytes long packets.
*
* @param callback the progress callback that will be notified each time a packet was sent.
* @return The request.
*/
@NonNull
public WriteRequest split(@NonNull final WriteProgressCallback callback) {
this.dataSplitter = MTU_SPLITTER;
this.progressCallback = callback;
return this;
}
/**
* This method makes sure the data sent will be split to at-most MTU-3 bytes long packets.
* This is because Long Write does not work with Reliable Write.
*/
void forceSplit() {
if (dataSplitter == null)
split();
}
/**
* Returns the next chunk to be sent. If data splitter was not set the date returned may
* be longer than MTU. Android will try to send them using Long Write sub-procedure if
* write type is {@link BluetoothGattCharacteristic#WRITE_TYPE_DEFAULT}. Other write types
* will cause the data to be truncated.
*
* @param mtu the current MTU.
* @return The next bytes to be sent.
*/
byte[] getData(@IntRange(from = 23, to = 517) final int mtu) {
if (dataSplitter == null || data == null) {
complete = true;
return currentChunk = data;
}
// Write Request and Write Command require 3 bytes for handler and op code.
// Write Signed requires 12 bytes, as the signature is sent.
final int maxLength = writeType != BluetoothGattCharacteristic.WRITE_TYPE_SIGNED ?
mtu - 3 : mtu - 12;
byte[] chunk = nextChunk;
// Get the first chunk.
if (chunk == null) {
chunk = dataSplitter.chunk(data, count, maxLength);
}
// If there's something to send, check if there are any more packets to be sent later.
if (chunk != null) {
nextChunk = dataSplitter.chunk(data, count + 1, maxLength);
}
// If there's no next packet left, we are done.
if (nextChunk == null) {
complete = true;
}
return currentChunk = chunk;
}
/**
* Method called when packet has been sent and confirmed (when Write With Response was used),
* or added to local outgoing buffer (when Write Without Response was used).
*
* @param device the target device.
* @param data the data received in the
* {@link android.bluetooth.BluetoothGattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int)}.
* @return True, if the data received are equal to data sent.
*/
boolean notifyPacketSent(@NonNull final BluetoothDevice device, @Nullable final byte[] data) {
if (progressCallback != null)
progressCallback.onPacketSent(device, data, count);
count++;
if (complete && valueCallback != null)
valueCallback.onDataSent(device, new Data(WriteRequest.this.data));
return Arrays.equals(data, currentChunk);
}
/**
* Returns whether there are more bytes to be sent from this Write Request.
* @return True if not all data were sent, false if the request is complete.
*/
boolean hasMore() {
return !complete;
}
/**
* Returns the write type that should be used to send the data.
* @return The write type.
*/
@WriteType
int getWriteType() {
return writeType;
}
private static byte[] copy(@Nullable final byte[] value,
@IntRange(from = 0) final int offset,
@IntRange(from = 0) final int length) {
if (value == null || offset > value.length)
return null;
final int maxLength = Math.min(value.length - offset, length);
final byte[] copy = new byte[maxLength];
System.arraycopy(value, offset, copy, 0, maxLength);
return copy;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
import no.nordicsemi.android.ble.ConnectionPriorityRequest;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
ConnectionPriorityRequest.CONNECTION_PRIORITY_BALANCED,
ConnectionPriorityRequest.CONNECTION_PRIORITY_HIGH,
ConnectionPriorityRequest.CONNECTION_PRIORITY_LOW_POWER
})
public @interface ConnectionPriority {}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.annotation;
import android.bluetooth.BluetoothProfile;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTING,
})
public @interface ConnectionState {}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
import no.nordicsemi.android.ble.PhyRequest;
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
PhyRequest.PHY_LE_1M_MASK,
PhyRequest.PHY_LE_2M_MASK,
PhyRequest.PHY_LE_CODED_MASK
})
public @interface PhyMask {}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
import no.nordicsemi.android.ble.PhyRequest;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
PhyRequest.PHY_OPTION_NO_PREFERRED,
PhyRequest.PHY_OPTION_S2,
PhyRequest.PHY_OPTION_S8
})
public @interface PhyOption {}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
import no.nordicsemi.android.ble.callback.PhyCallback;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
PhyCallback.PHY_LE_1M,
PhyCallback.PHY_LE_2M,
PhyCallback.PHY_LE_CODED
})
public @interface PhyValue {}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.annotation;
import android.bluetooth.BluetoothGattCharacteristic;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,
BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE,
BluetoothGattCharacteristic.WRITE_TYPE_SIGNED,
})
public @interface WriteType {}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
public interface BeforeCallback {
/**
* A callback invoked when the request is about to start.
*
* @param device the target device.
*/
void onRequestStarted(@NonNull final BluetoothDevice device);
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
/**
* The connection parameters for a Bluetooth LE connection is a set of parameters that determine
* when and how the Central and a Peripheral in a link transmits data.
* It is always the Central that actually sets the connection parameters used, but the Peripheral
* can send a so-called Connection Parameter Update Request, that the Central can then accept or reject.
* <p>
* On Android, requesting connection parameters is available since Android Lollipop using
* {@link android.bluetooth.BluetoothGatt#requestConnectionPriority(int)}. There are 3 options
* available: {@link android.bluetooth.BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER},
* {@link android.bluetooth.BluetoothGatt#CONNECTION_PRIORITY_BALANCED} and
* {@link android.bluetooth.BluetoothGatt#CONNECTION_PRIORITY_HIGH}. See
* {@link no.nordicsemi.android.ble.Request#newConnectionPriorityRequest(int)} for details.
* <p>
* Until Android 8.0 Oreo, there was no callback indicating whether the change has succeeded,
* or not. Also, when a Central or Peripheral requested connection parameters change without
* explicit calling of this method, the application was not aware of it.
* Android Oreo added a hidden callback to {@link android.bluetooth.BluetoothGattCallback}
* notifying about connection parameters change. Those values will be reported with this callback.
*/
public interface ConnectionPriorityCallback {
/**
* Callback indicating the connection parameters were updated. Works on Android 8.0 Oreo or newer.
*
* @param device the target device.
* @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
* 6 (7.5ms) to 3200 (4000ms).
* @param latency Slave latency for the connection in number of connection events. Valid range
* is from 0 to 499.
* @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
* (100 ms = 0.1s) to 3200 (32s).
*/
void onConnectionUpdated(@NonNull final BluetoothDevice device,
@IntRange(from = 6, to = 3200) final int interval,
@IntRange(from = 0, to = 499) final int latency,
@IntRange(from = 10, to = 3200) final int timeout);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataMerger;
public interface DataReceivedCallback {
/**
* Callback received each time the value was read or has changed using
* notifications or indications.
*
* @param device the target device.
* @param data the data received. If the {@link DataMerger} was used,
* this contains the merged result.
*/
void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataSplitter;
public interface DataSentCallback {
/**
* Callback received each time the value was written to a characteristic or descriptor.
*
* @param device the target device.
* @param data the data sent. If the {@link DataSplitter} was used, this contains the full data.
*/
void onDataSent(@NonNull final BluetoothDevice device, @NonNull final Data data);
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
public interface FailCallback {
int REASON_DEVICE_DISCONNECTED = -1;
int REASON_DEVICE_NOT_SUPPORTED = -2;
int REASON_NULL_ATTRIBUTE = -3;
int REASON_REQUEST_FAILED = -4;
int REASON_TIMEOUT = -5;
int REASON_VALIDATION = -6;
int REASON_BLUETOOTH_DISABLED = -100;
/**
* A callback invoked when the request has failed with status other than
* {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
*
* @param device target device.
* @param status error status code, one of BluetoothGatt#GATT_* constants or
* {@link #REASON_DEVICE_DISCONNECTED}, {@link #REASON_TIMEOUT},
* {@link #REASON_DEVICE_NOT_SUPPORTED} (only for Connect request),
* {@link #REASON_BLUETOOTH_DISABLED}, {@link #REASON_NULL_ATTRIBUTE},
* {@link #REASON_VALIDATION} or {@link #REASON_REQUEST_FAILED} (for other reason).
*/
void onRequestFailed(@NonNull final BluetoothDevice device, final int status);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
public interface InvalidRequestCallback {
/**
* A callback invoked when the request was invalid, for example when was called before the
* device was connected.
*/
void onInvalidRequest();
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
public interface MtuCallback {
/**
* Method called when the MTU request has finished with success. The MTU value may
* be different than requested one. The maximum packet size is 3 bytes less then MTU.
*
* @param device the target device.
* @param mtu the new MTU (Maximum Transfer Unit).
*/
void onMtuChanged(@NonNull final BluetoothDevice device,
@IntRange(from = 23, to = 517) final int mtu);
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.annotation.PhyValue;
@SuppressWarnings("unused")
public interface PhyCallback {
/**
* Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or
* connection.
*/
int PHY_LE_1M = 1;
/**
* Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or
* connection.
*/
int PHY_LE_2M = 2;
/**
* Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning
* or connection.
*/
int PHY_LE_CODED = 3;
/**
* Method called when the PHY value has changed or was read.
*
* @param device the target device.
* @param txPhy the transmitter PHY in use. One of {@link #PHY_LE_1M},
* {@link #PHY_LE_2M}, and {@link #PHY_LE_CODED}.
* @param rxPhy the receiver PHY in use. One of {@link #PHY_LE_1M},
* {@link #PHY_LE_2M}, and {@link #PHY_LE_CODED}.
*/
void onPhyChanged(@NonNull final BluetoothDevice device,
@PhyValue final int txPhy, @PhyValue final int rxPhy);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.data.DataMerger;
public interface ReadProgressCallback {
/**
* Callback received each time the value was read or has changed using notifications or
* indications when {@link DataMerger} was used.
*
* @param device the target device.
* @param data the last packet received.
* @param index the index of a packet that will be merged into a single Data.
*/
void onPacketReceived(@NonNull final BluetoothDevice device,
@Nullable final byte[] data, @IntRange(from = 0) final int index);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
public interface RssiCallback {
/**
* Method called when the RSSI value has been read.
*
* @param device the target device.
* @param rssi the current RSSI value, in dBm.
*/
void onRssiRead(@NonNull final BluetoothDevice device,
@IntRange(from = -128, to = 20) final int rssi);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
public interface SuccessCallback {
/**
* A callback invoked when the request completed successfully.
*
* @param device the target device.
*/
void onRequestCompleted(@NonNull final BluetoothDevice device);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.data.DataSplitter;
public interface WriteProgressCallback {
/**
* Callback called each time a packet has been sent when {@link DataSplitter} was used.
*
* @param device the target device.
* @param data the last packet sent.
* @param index the index of a packet that the initial Data was cut into.
*/
void onPacketSent(@NonNull final BluetoothDevice device,
@Nullable final byte[] data, @IntRange(from = 0) final int index);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback.profile;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.callback.DataReceivedCallback;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataMerger;
@SuppressWarnings("unused")
public interface ProfileDataCallback extends DataReceivedCallback {
/**
* Callback called when the data received do not conform to required scheme.
* @param device the target device.
* @param data the data received. If the {@link DataMerger} was used, this contains the
* merged result.
*/
default void onInvalidDataReceived(@NonNull final BluetoothDevice device,
@NonNull final Data data) {
// ignore
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.callback.profile;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.response.ReadResponse;
/**
* A response type for read requests with basic validation check.
* When read was requested as a synchronous call the {@link #isValid()} can be used to
* check if data were parsed successfully. Parsing method must call super methods on
* both {@link #onDataReceived(BluetoothDevice, Data)} and
* {@link #onInvalidDataReceived(BluetoothDevice, Data)} in order to make getters working properly.
* <p>
* Check out profile data callbacks in the Android BLE Common Library for example of usage.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class ProfileReadResponse extends ReadResponse implements ProfileDataCallback, Parcelable {
private boolean valid = true;
public ProfileReadResponse() {
// empty
}
@Override
public void onInvalidDataReceived(@NonNull final BluetoothDevice device,
@NonNull final Data data) {
this.valid = false;
}
/**
* Returns true if {@link #onInvalidDataReceived(BluetoothDevice, Data)} wasn't called.
*
* @return True, if profile data were valid, false if parsing error occurred.
*/
public boolean isValid() {
return valid;
}
// Parcelable
protected ProfileReadResponse(final Parcel in) {
super(in);
valid = in.readByte() != 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
super.writeToParcel(dest, flags);
dest.writeByte((byte) (valid ? 1 : 0));
}
public static final Creator<ProfileReadResponse> CREATOR = new Creator<ProfileReadResponse>() {
@Override
public ProfileReadResponse createFromParcel(final Parcel in) {
return new ProfileReadResponse(in);
}
@Override
public ProfileReadResponse[] newArray(final int size) {
return new ProfileReadResponse[size];
}
};
}

View File

@@ -0,0 +1,470 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.data;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@SuppressWarnings({"WeakerAccess", "unused", "UnusedReturnValue"})
public class Data implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
FORMAT_UINT8,
FORMAT_UINT16,
FORMAT_UINT24,
FORMAT_UINT32,
FORMAT_SINT8,
FORMAT_SINT16,
FORMAT_SINT24,
FORMAT_SINT32,
FORMAT_FLOAT,
FORMAT_SFLOAT
})
public @interface ValueFormat {}
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
FORMAT_UINT8,
FORMAT_UINT16,
FORMAT_UINT24,
FORMAT_UINT32,
FORMAT_SINT8,
FORMAT_SINT16,
FORMAT_SINT24,
FORMAT_SINT32
})
public @interface IntFormat {}
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
FORMAT_UINT32,
FORMAT_SINT32
})
public @interface LongFormat {}
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
FORMAT_FLOAT,
FORMAT_SFLOAT
})
public @interface FloatFormat {}
private static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
/**
* Data value format type uint8
*/
public final static int FORMAT_UINT8 = 0x11;
/**
* Data value format type uint16
*/
public final static int FORMAT_UINT16 = 0x12;
/**
* Data value format type uint24
*/
public final static int FORMAT_UINT24 = 0x13;
/**
* Data value format type uint32
*/
public final static int FORMAT_UINT32 = 0x14;
/**
* Data value format type sint8
*/
public final static int FORMAT_SINT8 = 0x21;
/**
* Data value format type sint16
*/
public final static int FORMAT_SINT16 = 0x22;
/**
* Data value format type sint24
*/
public final static int FORMAT_SINT24 = 0x23;
/**
* Data value format type sint32
*/
public final static int FORMAT_SINT32 = 0x24;
/**
* Data value format type sfloat (16-bit float, IEEE-11073)
*/
public final static int FORMAT_SFLOAT = 0x32;
/**
* Data value format type float (32-bit float, IEEE-11073)
*/
public final static int FORMAT_FLOAT = 0x34;
protected byte[] mValue;
public Data() {
this.mValue = null;
}
public Data(@Nullable final byte[] value) {
this.mValue = value;
}
public static Data from(@NonNull final String value) {
return new Data(value.getBytes()); // UTF-8
}
public static Data from(@NonNull final BluetoothGattCharacteristic characteristic) {
return new Data(characteristic.getValue());
}
public static Data from(@NonNull final BluetoothGattDescriptor descriptor) {
return new Data(descriptor.getValue());
}
public static Data opCode(final byte opCode) {
return new Data(new byte[] { opCode });
}
public static Data opCode(final byte opCode, final byte parameter) {
return new Data(new byte[] { opCode, parameter });
}
/**
* Returns the underlying byte array.
*
* @return Data received.
*/
@Nullable
public byte[] getValue() {
return mValue;
}
/**
* Return the stored value of this characteristic.
* <p>See {@link #getValue} for details.
*
* @param offset Offset at which the string value can be found.
* @return Cached value of the characteristic
*/
@Nullable
public String getStringValue(@IntRange(from = 0) final int offset) {
if (mValue == null || offset > mValue.length)
return null;
final byte[] strBytes = new byte[mValue.length - offset];
for (int i = 0; i != (mValue.length - offset); ++i)
strBytes[i] = mValue[offset+i];
return new String(strBytes);
}
/**
* Returns the size of underlying byte array.
*
* @return Length of the data.
*/
public int size() {
return mValue != null ? mValue.length : 0;
}
@Override
public String toString() {
if (size() == 0)
return "";
final char[] out = new char[mValue.length * 3 - 1];
for (int j = 0; j < mValue.length; j++) {
int v = mValue[j] & 0xFF;
out[j * 3] = HEX_ARRAY[v >>> 4];
out[j * 3 + 1] = HEX_ARRAY[v & 0x0F];
if (j != mValue.length - 1)
out[j * 3 + 2] = '-';
}
return "(0x) " + new String(out);
}
/**
* Returns a byte at the given offset from the byte array.
*
* @param offset Offset at which the byte value can be found.
* @return Cached value or null of offset exceeds value size.
*/
@Nullable
public Byte getByte(@IntRange(from = 0) final int offset) {
if (offset + 1 > size()) return null;
return mValue[offset];
}
/**
* Returns an integer value from the byte array.
* <p>
* <p>The formatType parameter determines how the value
* is to be interpreted. For example, setting formatType to
* {@link #FORMAT_UINT16} specifies that the first two bytes of the
* value at the given offset are interpreted to generate the
* return value.
*
* @param formatType The format type used to interpret the value.
* @param offset Offset at which the integer value can be found.
* @return Cached value or null of offset exceeds value size.
*/
@Nullable
public Integer getIntValue(@IntFormat final int formatType,
@IntRange(from = 0) final int offset) {
if ((offset + getTypeLen(formatType)) > size()) return null;
switch (formatType) {
case FORMAT_UINT8:
return unsignedByteToInt(mValue[offset]);
case FORMAT_UINT16:
return unsignedBytesToInt(mValue[offset], mValue[offset + 1]);
case FORMAT_UINT24:
return unsignedBytesToInt(mValue[offset], mValue[offset + 1],
mValue[offset + 2], (byte) 0);
case FORMAT_UINT32:
return unsignedBytesToInt(mValue[offset], mValue[offset + 1],
mValue[offset + 2], mValue[offset + 3]);
case FORMAT_SINT8:
return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8);
case FORMAT_SINT16:
return unsignedToSigned(unsignedBytesToInt(mValue[offset],
mValue[offset + 1]), 16);
case FORMAT_SINT24:
return unsignedToSigned(unsignedBytesToInt(mValue[offset],
mValue[offset + 1], mValue[offset + 2], (byte) 0), 24);
case FORMAT_SINT32:
return unsignedToSigned(unsignedBytesToInt(mValue[offset],
mValue[offset + 1], mValue[offset + 2], mValue[offset + 3]), 32);
}
return null;
}
/**
* Returns a long value from the byte array.
* <p>Only {@link #FORMAT_UINT32} and {@link #FORMAT_SINT32} are supported.
* <p>The formatType parameter determines how the value
* is to be interpreted. For example, setting formatType to
* {@link #FORMAT_UINT32} specifies that the first four bytes of the
* value at the given offset are interpreted to generate the
* return value.
*
* @param formatType The format type used to interpret the value.
* @param offset Offset at which the integer value can be found.
* @return Cached value or null of offset exceeds value size.
*/
@Nullable
public Long getLongValue(@LongFormat final int formatType,
@IntRange(from = 0) final int offset) {
if ((offset + getTypeLen(formatType)) > size()) return null;
switch (formatType) {
case FORMAT_SINT32:
return unsignedToSigned(unsignedBytesToLong(mValue[offset],
mValue[offset + 1], mValue[offset + 2], mValue[offset + 3]), 32);
case FORMAT_UINT32:
return unsignedBytesToLong(mValue[offset], mValue[offset + 1],
mValue[offset + 2], mValue[offset + 3]);
}
return null;
}
/**
* Returns an float value from the given byte array.
*
* @param formatType The format type used to interpret the value.
* @param offset Offset at which the float value can be found.
* @return Cached value at a given offset or null if the requested offset exceeds the value size.
*/
@Nullable
public Float getFloatValue(@FloatFormat final int formatType,
@IntRange(from = 0) final int offset) {
if ((offset + getTypeLen(formatType)) > size()) return null;
switch (formatType) {
case FORMAT_SFLOAT:
if (mValue[offset + 1] == 0x07 && mValue[offset] == (byte) 0xFE)
return Float.POSITIVE_INFINITY;
if ((mValue[offset + 1] == 0x07 && mValue[offset] == (byte) 0xFF) ||
(mValue[offset + 1] == 0x08 && mValue[offset] == 0x00) ||
(mValue[offset + 1] == 0x08 && mValue[offset] == 0x01))
return Float.NaN;
if (mValue[offset + 1] == 0x08 && mValue[offset] == 0x02)
return Float.NEGATIVE_INFINITY;
return bytesToFloat(mValue[offset], mValue[offset + 1]);
case FORMAT_FLOAT:
if (mValue[offset + 3] == 0x00) {
if (mValue[offset + 2] == 0x7F && mValue[offset + 1] == (byte) 0xFF) {
if (mValue[offset] == (byte) 0xFE)
return Float.POSITIVE_INFINITY;
if (mValue[offset] == (byte) 0xFF)
return Float.NaN;
} else if (mValue[offset + 2] == (byte) 0x80 && mValue[offset + 1] == 0x00) {
if (mValue[offset] == 0x00 || mValue[offset] == 0x01)
return Float.NaN;
if (mValue[offset] == 0x02)
return Float.NEGATIVE_INFINITY;
}
}
return bytesToFloat(mValue[offset], mValue[offset + 1],
mValue[offset + 2], mValue[offset + 3]);
}
return null;
}
/**
* Returns the size of a give value type.
*/
public static int getTypeLen(@ValueFormat final int formatType) {
return formatType & 0xF;
}
/**
* Convert a signed byte to an unsigned int.
*/
private static int unsignedByteToInt(final byte b) {
return b & 0xFF;
}
/**
* Convert a signed byte to an unsigned int.
*/
private static long unsignedByteToLong(final byte b) {
return b & 0xFFL;
}
/**
* Convert signed bytes to a 16-bit unsigned int.
*/
private static int unsignedBytesToInt(final byte b0, final byte b1) {
return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8));
}
/**
* Convert signed bytes to a 32-bit unsigned int.
*/
private static int unsignedBytesToInt(final byte b0, final byte b1, final byte b2, final byte b3) {
return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8))
+ (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24);
}
/**
* Convert signed bytes to a 32-bit unsigned long.
*/
private static long unsignedBytesToLong(final byte b0, final byte b1, final byte b2, final byte b3) {
return (unsignedByteToLong(b0) + (unsignedByteToLong(b1) << 8))
+ (unsignedByteToLong(b2) << 16) + (unsignedByteToLong(b3) << 24);
}
/**
* Convert signed bytes to a 16-bit short float value.
*/
private static float bytesToFloat(final byte b0, final byte b1) {
int mantissa = unsignedToSigned(unsignedByteToInt(b0)
+ ((unsignedByteToInt(b1) & 0x0F) << 8), 12);
int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4);
return (float) (mantissa * Math.pow(10, exponent));
}
/**
* Convert signed bytes to a 32-bit short float value.
*/
private static float bytesToFloat(final byte b0, final byte b1, final byte b2, final byte b3) {
int mantissa = unsignedToSigned(unsignedByteToInt(b0)
+ (unsignedByteToInt(b1) << 8)
+ (unsignedByteToInt(b2) << 16), 24);
return (float) (mantissa * Math.pow(10, b3));
}
/**
* Convert an unsigned integer value to a two's-complement encoded
* signed value.
*/
private static int unsignedToSigned(int unsigned, final int size) {
if ((unsigned & (1 << size - 1)) != 0) {
unsigned = -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
}
return unsigned;
}
/**
* Convert an unsigned long value to a two's-complement encoded
* signed value.
*/
private static long unsignedToSigned(long unsigned, final int size) {
if ((unsigned & (1 << size - 1)) != 0) {
unsigned = -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
}
return unsigned;
}
// Parcelable
protected Data(final Parcel in) {
mValue = in.createByteArray();
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeByteArray(mValue);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Data> CREATOR = new Creator<Data>() {
@Override
public Data createFromParcel(final Parcel in) {
return new Data(in);
}
@Override
public Data[] newArray(final int size) {
return new Data[size];
}
};
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.data;
import androidx.annotation.Nullable;
public interface DataFilter {
/**
* This method should return true if the packet matches the filter and should be processed
* by the request.
*
* @param lastPacket the packet received.
* @return True, if packet should be processed, false if it should be ignored.
*/
boolean filter(@Nullable final byte[] lastPacket);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.data;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public interface DataMerger {
/**
* This method should merge the last packet into the output message.
*
* @param output the stream for the output message, initially empty.
* @param lastPacket the data received in the last read/notify/indicate operation.
* @param index an index of the packet, 0-based (if you expect 3 packets, they will be
* called with indexes 0, 1, 2).
* @return True, if the message is complete, false if more data are expected.
*/
boolean merge(@NonNull final DataStream output,
@Nullable final byte[] lastPacket, @IntRange(from = 0) final int index);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.data;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public interface DataSplitter {
/**
* The implementation should return a index'th byte array from given message,
* with at most maxLength size, or null if no bytes are left to be sent.
*
* @param message the full message to be chunk.
* @param index index of a packet, 0-based.
* @param maxLength maximum length of the returned packet. Equals to MTU-3.
* Use {@link no.nordicsemi.android.ble.BleManager#requestMtu(int)} to request
* higher MTU, or {@link no.nordicsemi.android.ble.BleManager#overrideMtu(int)}
* If the MTU change was initiated by the target device.
* @return The packet to be sent, or null, if the whole message was already split.
*/
@Nullable
byte[] chunk(@NonNull final byte[] message,
@IntRange(from = 0) final int index, @IntRange(from = 20) final int maxLength);
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.data;
import java.io.ByteArrayOutputStream;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@SuppressWarnings("WeakerAccess")
public class DataStream {
private final ByteArrayOutputStream buffer;
public DataStream() {
buffer = new ByteArrayOutputStream();
}
@SuppressWarnings("SimplifiableIfStatement")
public boolean write(@Nullable final byte[] data) {
if (data == null)
return false;
return write(data, 0, data.length);
}
public boolean write(@Nullable final byte[] data,
@IntRange(from = 0) final int offset, @IntRange(from = 0) final int length) {
if (data == null || data.length < offset)
return false;
final int len = Math.min(data.length - offset, length);
buffer.write(data, offset, len);
return true;
}
public boolean write(@Nullable final Data data) {
return data != null && write(data.getValue());
}
@IntRange(from = 0)
public int size() {
return buffer.size();
}
@NonNull
public byte[] toByteArray() {
return buffer.toByteArray();
}
@NonNull
public Data toData() {
return new Data(buffer.toByteArray());
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.data;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Splits the message into at-most MTU-3 size packets.
*/
public final class DefaultMtuSplitter implements DataSplitter {
@Nullable
@Override
public byte[] chunk(@NonNull final byte[] message,
@IntRange(from = 0) final int index,
@IntRange(from = 20) final int maxLength) {
final int offset = index * maxLength;
final int length = Math.min(maxLength, message.length - offset);
if (length <= 0)
return null;
final byte[] data = new byte[length];
System.arraycopy(message, offset, data, 0, length);
return data;
}
}

View File

@@ -0,0 +1,409 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.data;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@SuppressWarnings({"unused", "SameParameterValue", "WeakerAccess", "UnusedReturnValue"})
public class MutableData extends Data {
// Values required to convert float to IEEE-11073 SFLOAT
private final static int SFLOAT_POSITIVE_INFINITY = 0x07FE;
private final static int SFLOAT_NAN = 0x07FF;
// private final static int SFLOAT_NOT_AT_THIS_RESOLUTION = 0x0800;
// private final static int SFLOAT_RESERVED_VALUE = 0x0801;
private final static int SFLOAT_NEGATIVE_INFINITY = 0x0802;
private final static int SFLOAT_MANTISSA_MAX = 0x07FD;
private final static int SFLOAT_EXPONENT_MAX = 7;
private final static int SFLOAT_EXPONENT_MIN = -8;
private final static float SFLOAT_MAX = 20450000000.0f;
private final static float SFLOAT_MIN = -SFLOAT_MAX;
private final static int SFLOAT_PRECISION = 10000;
// private final static float SFLOAT_EPSILON = 1e-8f;
// Values required to convert float to IEEE-11073 FLOAT
private final static int FLOAT_POSITIVE_INFINITY = 0x007FFFFE;
private final static int FLOAT_NAN = 0x007FFFFF;
// private final static int FLOAT_NOT_AT_THIS_RESOLUTION = 0x00800000;
// private final static int FLOAT_RESERVED_VALUE = 0x00800001;
private final static int FLOAT_NEGATIVE_INFINITY = 0x00800002;
private final static int FLOAT_MANTISSA_MAX = 0x007FFFFD;
private final static int FLOAT_EXPONENT_MAX = 127;
private final static int FLOAT_EXPONENT_MIN = -128;
private final static int FLOAT_PRECISION = 10000000;
// private final static float FLOAT_EPSILON = 1e-128f;
public MutableData() {
super();
}
public MutableData(@Nullable final byte[] data) {
super(data);
}
public static MutableData from(@NonNull final BluetoothGattCharacteristic characteristic) {
return new MutableData(characteristic.getValue());
}
public static MutableData from(@NonNull final BluetoothGattDescriptor descriptor) {
return new MutableData(descriptor.getValue());
}
/**
* Updates the locally stored value of this data.
*
* @param value New value
* @return true if the locally stored value has been set, false if the
* requested value could not be stored locally.
*/
public boolean setValue(@Nullable final byte[] value) {
mValue = value;
return true;
}
/**
* Updates the byte at offset position.
*
* @param value Byte to set
* @param offset The offset
* @return true if the locally stored value has been set, false if the
* requested value could not be stored locally.
*/
public boolean setByte(final int value, @IntRange(from = 0) final int offset) {
final int len = offset + 1;
if (mValue == null) mValue = new byte[len];
if (len > mValue.length) return false;
mValue[offset] = (byte) value;
return true;
}
/**
* Set the locally stored value of this data.
* <p>See {@link #setValue(byte[])} for details.
*
* @param value New value for this data
* @param formatType Integer format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
*/
public boolean setValue(int value, @IntFormat int formatType, @IntRange(from = 0) int offset) {
final int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
if (len > mValue.length) return false;
switch (formatType) {
case FORMAT_SINT8:
value = intToSignedBits(value, 8);
// Fall-through intended
case FORMAT_UINT8:
mValue[offset] = (byte) (value & 0xFF);
break;
case FORMAT_SINT16:
value = intToSignedBits(value, 16);
// Fall-through intended
case FORMAT_UINT16:
mValue[offset++] = (byte) (value & 0xFF);
mValue[offset] = (byte) ((value >> 8) & 0xFF);
break;
case FORMAT_SINT24:
value = intToSignedBits(value, 24);
// Fall-through intended
case FORMAT_UINT24:
mValue[offset++] = (byte) (value & 0xFF);
mValue[offset++] = (byte) ((value >> 8) & 0xFF);
mValue[offset] = (byte) ((value >> 16) & 0xFF);
break;
case FORMAT_SINT32:
value = intToSignedBits(value, 32);
// Fall-through intended
case FORMAT_UINT32:
mValue[offset++] = (byte) (value & 0xFF);
mValue[offset++] = (byte) ((value >> 8) & 0xFF);
mValue[offset++] = (byte) ((value >> 16) & 0xFF);
mValue[offset] = (byte) ((value >> 24) & 0xFF);
break;
default:
return false;
}
return true;
}
/**
* Set the locally stored value of this data.
* <p>See {@link #setValue(byte[])} for details.
*
* @param mantissa Mantissa for this data
* @param exponent Exponent value for this data
* @param formatType Float format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
*/
public boolean setValue(int mantissa, int exponent,
@FloatFormat int formatType, @IntRange(from = 0) int offset) {
final int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
if (len > mValue.length) return false;
switch (formatType) {
case FORMAT_SFLOAT:
mantissa = intToSignedBits(mantissa, 12);
exponent = intToSignedBits(exponent, 4);
mValue[offset++] = (byte) (mantissa & 0xFF);
mValue[offset] = (byte) ((mantissa >> 8) & 0x0F);
mValue[offset] += (byte) ((exponent & 0x0F) << 4);
break;
case FORMAT_FLOAT:
mantissa = intToSignedBits(mantissa, 24);
exponent = intToSignedBits(exponent, 8);
mValue[offset++] = (byte) (mantissa & 0xFF);
mValue[offset++] = (byte) ((mantissa >> 8) & 0xFF);
mValue[offset++] = (byte) ((mantissa >> 16) & 0xFF);
mValue[offset] += (byte) (exponent & 0xFF);
break;
default:
return false;
}
return true;
}
/**
* Set the locally stored value of this data.
* <p>See {@link #setValue(byte[])} for details.
*
* @param value New value for this data. This allows to send {@link #FORMAT_UINT32}.
* @param formatType Integer format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
*/
public boolean setValue(long value, @LongFormat int formatType, @IntRange(from = 0) int offset) {
final int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
if (len > mValue.length) return false;
switch (formatType) {
case FORMAT_SINT32:
value = longToSignedBits(value, 32);
// Fall-through intended
case FORMAT_UINT32:
mValue[offset++] = (byte) (value & 0xFF);
mValue[offset++] = (byte) ((value >> 8) & 0xFF);
mValue[offset++] = (byte) ((value >> 16) & 0xFF);
mValue[offset] = (byte) ((value >> 24) & 0xFF);
break;
default:
return false;
}
return true;
}
/**
* Set the locally stored value of this data.
* <p>See {@link #setValue(byte[])} for details.
*
* @param value Float value to be written
* @param formatType Float format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
*/
public boolean setValue(float value,
@FloatFormat int formatType, @IntRange(from = 0) int offset) {
final int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
if (len > mValue.length) return false;
switch (formatType) {
case FORMAT_SFLOAT:
final int sfloatAsInt = sfloatToInt(value);
mValue[offset++] = (byte) (sfloatAsInt & 0xFF);
mValue[offset] = (byte) ((sfloatAsInt >> 8) & 0xFF);
break;
case FORMAT_FLOAT:
final int floatAsInt = floatToInt(value);
mValue[offset++] = (byte) (floatAsInt & 0xFF);
mValue[offset++] = (byte) ((floatAsInt >> 8) & 0xFF);
mValue[offset++] = (byte) ((floatAsInt >> 16) & 0xFF);
mValue[offset] += (byte) ((floatAsInt >> 24) & 0xFF);
break;
default:
return false;
}
return true;
}
/**
* Converts float to SFLOAT IEEE 11073 format as UINT16, rounding up or down.
* See: https://github.com/signove/antidote/blob/master/src/util/bytelib.c
*
* @param value the value to be converted.
* @return given float as UINT16 in IEEE 11073 format.
*/
private static int sfloatToInt(final float value) {
if (Float.isNaN(value)) {
return SFLOAT_NAN;
} else if (value > SFLOAT_MAX) {
return SFLOAT_POSITIVE_INFINITY;
} else if (value < SFLOAT_MIN) {
return SFLOAT_NEGATIVE_INFINITY;
}
int sign = value >= 0 ? +1 : -1;
float mantissa = Math.abs(value);
int exponent = 0; // Note: 10**x exponent, not 2**x
// scale up if number is too big
while (mantissa > SFLOAT_MANTISSA_MAX) {
mantissa /= 10.0f;
++exponent;
if (exponent > SFLOAT_EXPONENT_MAX) {
// argh, should not happen
if (sign > 0) {
return SFLOAT_POSITIVE_INFINITY;
} else {
return SFLOAT_NEGATIVE_INFINITY;
}
}
}
// scale down if number is too small
while (mantissa < 1) {
mantissa *= 10;
--exponent;
if (exponent < SFLOAT_EXPONENT_MIN) {
// argh, should not happen
return 0;
}
}
// scale down if number needs more precision
double smantissa = Math.round(mantissa * SFLOAT_PRECISION);
double rmantissa = Math.round(mantissa) * SFLOAT_PRECISION;
double mdiff = Math.abs(smantissa - rmantissa);
while (mdiff > 0.5 && exponent > SFLOAT_EXPONENT_MIN &&
(mantissa * 10) <= SFLOAT_MANTISSA_MAX) {
mantissa *= 10;
--exponent;
smantissa = Math.round(mantissa * SFLOAT_PRECISION);
rmantissa = Math.round(mantissa) * SFLOAT_PRECISION;
mdiff = Math.abs(smantissa - rmantissa);
}
int int_mantissa = Math.round(sign * mantissa);
return ((exponent & 0xF) << 12) | (int_mantissa & 0xFFF);
}
/**
* Converts float to FLOAT IEEE 11073 format as UINT32, rounding up or down.
* See: https://github.com/signove/antidote/blob/master/src/util/bytelib.c
*
* @param value the value to be converted.
* @return given float as UINT32 in IEEE 11073 format.
*/
private static int floatToInt(final float value) {
if (Float.isNaN(value)) {
return FLOAT_NAN;
} else if (value == Float.POSITIVE_INFINITY) {
return FLOAT_POSITIVE_INFINITY;
} else if (value == Float.NEGATIVE_INFINITY) {
return FLOAT_NEGATIVE_INFINITY;
}
int sign = value >= 0 ? +1 : -1;
float mantissa = Math.abs(value);
int exponent = 0; // Note: 10**x exponent, not 2**x
// scale up if number is too big
while (mantissa > FLOAT_MANTISSA_MAX) {
mantissa /= 10.0f;
++exponent;
if (exponent > FLOAT_EXPONENT_MAX) {
// argh, should not happen
if (sign > 0) {
return FLOAT_POSITIVE_INFINITY;
} else {
return FLOAT_NEGATIVE_INFINITY;
}
}
}
// scale down if number is too small
while (mantissa < 1) {
mantissa *= 10;
--exponent;
if (exponent < FLOAT_EXPONENT_MIN) {
// argh, should not happen
return 0;
}
}
// scale down if number needs more precision
double smantissa = Math.round(mantissa * FLOAT_PRECISION);
double rmantissa = Math.round(mantissa) * FLOAT_PRECISION;
double mdiff = Math.abs(smantissa - rmantissa);
while (mdiff > 0.5 && exponent > FLOAT_EXPONENT_MIN &&
(mantissa * 10) <= FLOAT_MANTISSA_MAX) {
mantissa *= 10;
--exponent;
smantissa = Math.round(mantissa * FLOAT_PRECISION);
rmantissa = Math.round(mantissa) * FLOAT_PRECISION;
mdiff = Math.abs(smantissa - rmantissa);
}
int int_mantissa = Math.round(sign * mantissa);
return (exponent << 24) | (int_mantissa & 0xFFFFFF);
}
/**
* Convert an integer into the signed bits of a given length.
*/
private static int intToSignedBits(int i, int size) {
if (i < 0) {
i = (1 << size - 1) + (i & ((1 << size - 1) - 1));
}
return i;
}
/**
* Convert a long into the signed bits of a given length.
*/
private static long longToSignedBits(long i, int size) {
if (i < 0) {
i = (1L << size - 1) + (i & ((1L << size - 1) - 1));
}
return i;
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.error;
import android.bluetooth.BluetoothGatt;
/**
* Parses the GATT and HCI errors to human readable strings.
* <p>
* See: <a href="https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/stack/include/gatt_api.h">gatt_api.h</a> for details.<br>
* See also: <a href="https://android.googlesource.com/platform/external/libnfc-nci/+/master/src/include/hcidefs.h#447">hcidefs.h</a> for other possible HCI errors.
*/
@SuppressWarnings("WeakerAccess")
public class GattError {
public static final int GATT_SUCCESS = BluetoothGatt.GATT_SUCCESS;
public static final int GATT_CONN_L2C_FAILURE = 0x01;
public static final int GATT_CONN_TIMEOUT = 0x08;
public static final int GATT_CONN_TERMINATE_PEER_USER = 0x13;
public static final int GATT_CONN_TERMINATE_LOCAL_HOST = 0x16;
public static final int GATT_CONN_FAIL_ESTABLISH = 0x3E;
public static final int GATT_CONN_LMP_TIMEOUT = 0x22;
public static final int GATT_CONN_CANCEL = 0x0100;
public static final int GATT_ERROR = 0x0085; // Device not reachable
public static final int GATT_INVALID_HANDLE = 0x0001;
public static final int GATT_READ_NOT_PERMIT = 0x0002;
public static final int GATT_WRITE_NOT_PERMIT = 0x0003;
public static final int GATT_INVALID_PDU = 0x0004;
public static final int GATT_INSUF_AUTHENTICATION = 0x0005;
public static final int GATT_REQ_NOT_SUPPORTED = 0x0006;
public static final int GATT_INVALID_OFFSET = 0x0007;
public static final int GATT_INSUF_AUTHORIZATION = 0x0008;
public static final int GATT_PREPARE_Q_FULL = 0x0009;
public static final int GATT_NOT_FOUND = 0x000a;
public static final int GATT_NOT_LONG = 0x000b;
public static final int GATT_INSUF_KEY_SIZE = 0x000c;
public static final int GATT_INVALID_ATTR_LEN = 0x000d;
public static final int GATT_ERR_UNLIKELY = 0x000e;
public static final int GATT_INSUF_ENCRYPTION = 0x000f;
public static final int GATT_UNSUPPORT_GRP_TYPE = 0x0010;
public static final int GATT_INSUF_RESOURCE = 0x0011;
public static final int GATT_CONTROLLER_BUSY = 0x003A;
public static final int GATT_UNACCEPT_CONN_INTERVAL = 0x003B;
public static final int GATT_ILLEGAL_PARAMETER = 0x0087;
public static final int GATT_NO_RESOURCES = 0x0080;
public static final int GATT_INTERNAL_ERROR = 0x0081;
public static final int GATT_WRONG_STATE = 0x0082;
public static final int GATT_DB_FULL = 0x0083;
public static final int GATT_BUSY = 0x0084;
public static final int GATT_CMD_STARTED = 0x0086;
public static final int GATT_PENDING = 0x0088;
public static final int GATT_AUTH_FAIL = 0x0089;
public static final int GATT_MORE = 0x008a;
public static final int GATT_INVALID_CFG = 0x008b;
public static final int GATT_SERVICE_STARTED = 0x008c;
public static final int GATT_ENCRYPTED_NO_MITM = 0x008d;
public static final int GATT_NOT_ENCRYPTED = 0x008e;
public static final int GATT_CONGESTED = 0x008f;
public static final int GATT_CCCD_CFG_ERROR = 0x00FD;
public static final int GATT_PROCEDURE_IN_PROGRESS = 0x00FE;
public static final int GATT_VALUE_OUT_OF_RANGE = 0x00FF;
public static final int TOO_MANY_OPEN_CONNECTIONS = 0x0101;
/**
* Converts the connection status given by the
* {@link android.bluetooth.BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)}
* to error name.
*
* @param error the status number.
* @return The error name as stated in the links in {@link GattError} documentation.
*/
public static String parseConnectionError(final int error) {
switch (error) {
case GATT_SUCCESS:
return "SUCCESS";
case GATT_CONN_L2C_FAILURE:
return "GATT CONN L2C FAILURE";
case GATT_CONN_TIMEOUT:
return "GATT CONN TIMEOUT";
case GATT_CONN_TERMINATE_PEER_USER:
return "GATT CONN TERMINATE PEER USER";
case GATT_CONN_TERMINATE_LOCAL_HOST:
return "GATT CONN TERMINATE LOCAL HOST";
case GATT_CONN_FAIL_ESTABLISH:
return "GATT CONN FAIL ESTABLISH";
case GATT_CONN_LMP_TIMEOUT:
return "GATT CONN LMP TIMEOUT";
case GATT_CONN_CANCEL:
return "GATT CONN CANCEL ";
case GATT_ERROR:
return "GATT ERROR"; // Device not reachable
default:
return "UNKNOWN (" + error + ")";
}
}
/**
* Converts the Bluetooth communication status given by other BluetoothGattCallbacks to error
* name. It also parses the DFU errors.
*
* @param error the status number.
* @return The error name as stated in the links in {@link GattError} documentation.
*/
public static String parse(final int error) {
switch (error) {
case GATT_INVALID_HANDLE:
return "GATT INVALID HANDLE";
case GATT_READ_NOT_PERMIT:
return "GATT READ NOT PERMIT";
case GATT_WRITE_NOT_PERMIT:
return "GATT WRITE NOT PERMIT";
case GATT_INVALID_PDU:
return "GATT INVALID PDU";
case GATT_INSUF_AUTHENTICATION:
return "GATT INSUF AUTHENTICATION";
case GATT_REQ_NOT_SUPPORTED:
return "GATT REQ NOT SUPPORTED";
case GATT_INVALID_OFFSET:
return "GATT INVALID OFFSET";
case GATT_INSUF_AUTHORIZATION:
return "GATT INSUF AUTHORIZATION";
case GATT_PREPARE_Q_FULL:
return "GATT PREPARE Q FULL";
case GATT_NOT_FOUND:
return "GATT NOT FOUND";
case GATT_NOT_LONG:
return "GATT NOT LONG";
case GATT_INSUF_KEY_SIZE:
return "GATT INSUF KEY SIZE";
case GATT_INVALID_ATTR_LEN:
return "GATT INVALID ATTR LEN";
case GATT_ERR_UNLIKELY:
return "GATT ERR UNLIKELY";
case GATT_INSUF_ENCRYPTION:
return "GATT INSUF ENCRYPTION";
case GATT_UNSUPPORT_GRP_TYPE:
return "GATT UNSUPPORT GRP TYPE";
case GATT_INSUF_RESOURCE:
return "GATT INSUF RESOURCE";
case GATT_CONN_LMP_TIMEOUT:
return "GATT CONN LMP TIMEOUT";
case GATT_CONTROLLER_BUSY:
return "GATT CONTROLLER BUSY";
case GATT_UNACCEPT_CONN_INTERVAL:
return "GATT UNACCEPT CONN INTERVAL";
case GATT_ILLEGAL_PARAMETER:
return "GATT ILLEGAL PARAMETER";
case GATT_NO_RESOURCES:
return "GATT NO RESOURCES";
case GATT_INTERNAL_ERROR:
return "GATT INTERNAL ERROR";
case GATT_WRONG_STATE:
return "GATT WRONG STATE";
case GATT_DB_FULL:
return "GATT DB FULL";
case GATT_BUSY:
return "GATT BUSY";
case GATT_ERROR:
return "GATT ERROR";
case GATT_CMD_STARTED:
return "GATT CMD STARTED";
case GATT_PENDING:
return "GATT PENDING";
case GATT_AUTH_FAIL:
return "GATT AUTH FAIL";
case GATT_MORE:
return "GATT MORE";
case GATT_INVALID_CFG:
return "GATT INVALID CFG";
case GATT_SERVICE_STARTED:
return "GATT SERVICE STARTED";
case GATT_ENCRYPTED_NO_MITM:
return "GATT ENCRYPTED NO MITM";
case GATT_NOT_ENCRYPTED:
return "GATT NOT ENCRYPTED";
case GATT_CONGESTED:
return "GATT CONGESTED";
case GATT_CCCD_CFG_ERROR:
return "GATT CCCD CFG ERROR";
case GATT_PROCEDURE_IN_PROGRESS:
return "GATT PROCEDURE IN PROGRESS";
case GATT_VALUE_OUT_OF_RANGE:
return "GATT VALUE OUT OF RANGE";
case TOO_MANY_OPEN_CONNECTIONS:
return "TOO MANY OPEN CONNECTIONS";
default:
return "UNKNOWN (" + error + ")";
}
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.exception;
public class BluetoothDisabledException extends ConnectionException {
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.exception;
@SuppressWarnings("WeakerAccess")
public class ConnectionException extends Exception {
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.exception;
public class DeviceDisconnectedException extends ConnectionException {
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.exception;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.callback.profile.ProfileReadResponse;
@SuppressWarnings("unused")
public final class InvalidDataException extends Exception {
private final ProfileReadResponse response;
public InvalidDataException(@NonNull final ProfileReadResponse response) {
this.response = response;
}
public ProfileReadResponse getResponse() {
return response;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.exception;
import no.nordicsemi.android.ble.Request;
@SuppressWarnings({"WeakerAccess", "unused"})
public final class InvalidRequestException extends Exception {
private final Request request;
public InvalidRequestException(final Request request) {
super("Invalid request");
this.request = request;
}
/**
* Returns the invalid request.
* @return The invalid request.
*/
public Request getRequest() {
return request;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.exception;
import no.nordicsemi.android.ble.Request;
@SuppressWarnings({"WeakerAccess", "unused"})
public final class RequestFailedException extends Exception {
private final Request request;
private final int status;
public RequestFailedException(final Request request, final int status) {
super("Request failed with status " + status);
this.request = request;
this.status = status;
}
/**
* Returns the request status. One of {{@link android.bluetooth.BluetoothGatt}} GATT_*
* or {@link no.nordicsemi.android.ble.callback.FailCallback} REASON_* codes.
*
* @return Error code.
*/
public int getStatus() {
return status;
}
/**
* Returns the request that failed.
* @return The request that failed.
*/
public Request getRequest() {
return request;
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.response;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.ConnectionPriorityCallback;
/**
* The synchronous response type for connection priority requests.
*
* @see ConnectionPriorityCallback
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class ConnectionPriorityResponse implements ConnectionPriorityCallback, Parcelable {
private BluetoothDevice device;
@IntRange(from = 6, to = 3200)
private int interval;
@IntRange(from = 0, to = 499)
private int latency;
@IntRange(from = 10, to = 3200)
private int supervisionTimeout;
@Override
public void onConnectionUpdated(@NonNull final BluetoothDevice device,
@IntRange(from = 6, to = 3200) final int interval,
@IntRange(from = 0, to = 499) final int latency,
@IntRange(from = 10, to = 3200) final int timeout) {
this.device = device;
this.interval = interval;
this.latency = latency;
this.supervisionTimeout = timeout;
}
@Nullable
public BluetoothDevice getBluetoothDevice() {
return device;
}
/**
* The connection interval determines how often the Central will ask for data from the Peripheral.
* When the Peripheral requests an update, it supplies a maximum and a minimum wanted interval.
* The connection interval must be between 7.5 ms and 4 s.
*
* @return Connection interval used on this connection, 1.25ms unit.
* Valid range is from 6 (7.5ms) to 3200 (4000ms).
*/
@IntRange(from = 6, to = 3200)
public int getConnectionInterval() {
return interval;
}
/**
* By setting a non-zero slave latency, the Peripheral can choose to not answer when
* the Central asks for data up to the slave latency number of times.
* However, if the Peripheral has data to send, it can choose to send data at any time.
* This enables a peripheral to stay sleeping for a longer time, if it doesn't have data to send,
* but still send data fast if needed. The text book example of such device is for example
* keyboard and mice, which want to be sleeping for as long as possible when there is
* no data to send, but still have low latency (and for the mouse: low connection interval)
* when needed.
*
* @return Slave latency for the connection in number of connection events.
* Valid range is from 0 to 499.
*/
@IntRange(from = 0, to = 499)
public int getSlaveLatency() {
return latency;
}
/**
* This timeout determines the timeout from the last data exchange till a link is considered lost.
* A Central will not start trying to reconnect before the timeout has passed,
* so if you have a device which goes in and out of range often, and you need to notice when
* that happens, it might make sense to have a short timeout.
*
* @return Supervision timeout for this connection, in 10ms unit.
* Valid range is from 10 (100 ms = 0.1s) to 3200 (32s).
*/
@IntRange(from = 10, to = 3200)
public int getSupervisionTimeout() {
return supervisionTimeout;
}
// Parcelable
protected ConnectionPriorityResponse(final Parcel in) {
device = in.readParcelable(BluetoothDevice.class.getClassLoader());
interval = in.readInt();
latency = in.readInt();
supervisionTimeout = in.readInt();
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, flags);
dest.writeInt(interval);
dest.writeInt(latency);
dest.writeInt(supervisionTimeout);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<ConnectionPriorityResponse> CREATOR = new Creator<ConnectionPriorityResponse>() {
@Override
public ConnectionPriorityResponse createFromParcel(final Parcel in) {
return new ConnectionPriorityResponse(in);
}
@Override
public ConnectionPriorityResponse[] newArray(final int size) {
return new ConnectionPriorityResponse[size];
}
};
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.response;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.MtuCallback;
@SuppressWarnings({"unused", "WeakerAccess"})
public class MtuResult implements MtuCallback, Parcelable {
private BluetoothDevice device;
@IntRange(from = 23, to = 517)
private int mtu;
@Override
public void onMtuChanged(@NonNull final BluetoothDevice device,
@IntRange(from = 23, to = 517) final int mtu) {
this.device = device;
this.mtu = mtu;
}
@Nullable
public BluetoothDevice getBluetoothDevice() {
return device;
}
/**
* Returns the agreed MTU. The maximum packet size is 3 bytes less then MTU.
*
* @return The MTU.
*/
@IntRange(from = 23, to = 517)
public int getMtu() {
return mtu;
}
// Parcelable
protected MtuResult(final Parcel in) {
device = in.readParcelable(BluetoothDevice.class.getClassLoader());
mtu = in.readInt();
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, flags);
dest.writeInt(mtu);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<MtuResult> CREATOR = new Creator<MtuResult>() {
@Override
public MtuResult createFromParcel(final Parcel in) {
return new MtuResult(in);
}
@Override
public MtuResult[] newArray(final int size) {
return new MtuResult[size];
}
};
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.response;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.annotation.PhyValue;
import no.nordicsemi.android.ble.callback.PhyCallback;
@SuppressWarnings({"unused", "WeakerAccess"})
public class PhyResult implements PhyCallback, Parcelable {
private BluetoothDevice device;
@PhyValue
private int txPhy;
@PhyValue
private int rxPhy;
@Override
public void onPhyChanged(@NonNull final BluetoothDevice device,
@PhyValue final int txPhy, @PhyValue final int rxPhy) {
this.device = device;
this.txPhy = txPhy;
this.rxPhy = rxPhy;
}
@Nullable
public BluetoothDevice getBluetoothDevice() {
return device;
}
@PhyValue
public int getTxPhy() {
return txPhy;
}
@PhyValue
public int getRxPhy() {
return rxPhy;
}
// Parcelable
protected PhyResult(final Parcel in) {
device = in.readParcelable(BluetoothDevice.class.getClassLoader());
txPhy = in.readInt();
rxPhy = in.readInt();
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, flags);
dest.writeInt(txPhy);
dest.writeInt(rxPhy);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<PhyResult> CREATOR = new Creator<PhyResult>() {
@Override
public PhyResult createFromParcel(final Parcel in) {
return new PhyResult(in);
}
@Override
public PhyResult[] newArray(final int size) {
return new PhyResult[size];
}
};
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.response;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.DataReceivedCallback;
import no.nordicsemi.android.ble.data.Data;
/**
* Generic read response class that returns the data received and the device from which data
* were read.
* Overriding class must call super on {@link #onDataReceived(BluetoothDevice, Data)} in
* order to make getters work properly.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class ReadResponse implements DataReceivedCallback, Parcelable {
private BluetoothDevice device;
private Data data;
public ReadResponse() {
// empty
}
@Override
public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
this.device = device;
this.data = data;
}
@Nullable
public BluetoothDevice getBluetoothDevice() {
return device;
}
@Nullable
public Data getRawData() {
return data;
}
// Parcelable
protected ReadResponse(final Parcel in) {
device = in.readParcelable(BluetoothDevice.class.getClassLoader());
data = in.readParcelable(Data.class.getClassLoader());
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, flags);
dest.writeParcelable(data, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<ReadResponse> CREATOR = new Creator<ReadResponse>() {
@Override
public ReadResponse createFromParcel(final Parcel in) {
return new ReadResponse(in);
}
@Override
public ReadResponse[] newArray(final int size) {
return new ReadResponse[size];
}
};
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.response;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.RssiCallback;
@SuppressWarnings({"unused", "WeakerAccess"})
public class RssiResult implements RssiCallback, Parcelable {
private BluetoothDevice device;
@IntRange(from = -128, to = 20)
private int rssi;
@Override
public void onRssiRead(@NonNull final BluetoothDevice device,
@IntRange(from = -128, to = 20) final int rssi) {
this.device = device;
this.rssi = rssi;
}
@Nullable
public BluetoothDevice getBluetoothDevice() {
return device;
}
@IntRange(from = -128, to = 20)
public int getRssi() {
return rssi;
}
// Parcelable
protected RssiResult(final Parcel in) {
device = in.readParcelable(BluetoothDevice.class.getClassLoader());
rssi = in.readInt();
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, flags);
dest.writeInt(rssi);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<RssiResult> CREATOR = new Creator<RssiResult>() {
@Override
public RssiResult createFromParcel(final Parcel in) {
return new RssiResult(in);
}
@Override
public RssiResult[] newArray(final int size) {
return new RssiResult[size];
}
};
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.response;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import no.nordicsemi.android.ble.callback.DataSentCallback;
import no.nordicsemi.android.ble.data.Data;
/**
* Generic write response class that returns the target device and the data that were sent.
* Overriding class must call super on {@link #onDataSent(BluetoothDevice, Data)} in
* order to make getters work properly.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class WriteResponse implements DataSentCallback, Parcelable {
private BluetoothDevice device;
private Data data;
@Override
public void onDataSent(@NonNull final BluetoothDevice device, @NonNull final Data data) {
this.device = device;
this.data = data;
}
@Nullable
public BluetoothDevice getBluetoothDevice() {
return device;
}
@Nullable
public Data getRawData() {
return data;
}
// Parcelable
protected WriteResponse(final Parcel in) {
device = in.readParcelable(BluetoothDevice.class.getClassLoader());
data = in.readParcelable(Data.class.getClassLoader());
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, flags);
dest.writeParcelable(data, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<WriteResponse> CREATOR = new Creator<WriteResponse>() {
@Override
public WriteResponse createFromParcel(final Parcel in) {
return new WriteResponse(in);
}
@Override
public WriteResponse[] newArray(final int size) {
return new WriteResponse[size];
}
};
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@SuppressWarnings("unused")
public interface ILogger {
/**
* Logs the given message with given log priority into the all managed devices' log session.
*
* @param priority the log priority.
* @param message the message to be logged.
*/
void log(final int priority, @NonNull final String message);
/**
* Logs the given message with given log priority into the all managed devices' log session.
*
* @param priority the log priority.
* @param messageRes string resource id.
* @param params additional (optional) parameters used to fill the message.
*/
void log(final int priority, @StringRes final int messageRes, @Nullable final Object... params);
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.ble.utils;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
@SuppressWarnings({"WeakerAccess", "unused"})
public class ParserUtils {
protected final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String parse(final BluetoothGattCharacteristic characteristic) {
return parse(characteristic.getValue());
}
public static String parse(final BluetoothGattDescriptor descriptor) {
return parse(descriptor.getValue());
}
public static String parse(final byte[] data) {
if (data == null || data.length == 0)
return "";
final char[] out = new char[data.length * 3 - 1];
for (int j = 0; j < data.length; j++) {
int v = data[j] & 0xFF;
out[j * 3] = HEX_ARRAY[v >>> 4];
out[j * 3 + 1] = HEX_ARRAY[v & 0x0F];
if (j != data.length - 1)
out[j * 3 + 2] = '-';
}
return "(0x) " + new String(out);
}
}

181
main.qml
View File

@@ -1,6 +1,7 @@
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import QtQuick.Controls 2.4 import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
Window { Window {
id: window id: window
@@ -13,50 +14,87 @@ Window {
Column { Column {
id: column id: column
Label { Label {
text: "dist1: " + ((mgmt.dist1 ? mgmt.dist1+sld1.value : 0)/1000).toFixed(2); text: "FTM dist1: " + ((mgmt.dist1 ? mgmt.dist1+sld1.value : 0)/1000).toFixed(2);
} }
Label { Label {
text: "dist2: " + ((mgmt.dist2 ? mgmt.dist2+sld1.value : 0)/1000).toFixed(2); text: "FTM dist2: " + ((mgmt.dist2 ? mgmt.dist2+sld1.value : 0)/1000).toFixed(2);
} }
Label { Label {
text: "dist3: " + ((mgmt.dist3 ? mgmt.dist3+sld1.value : 0)/1000).toFixed(2); text: "FTM dist3: " + ((mgmt.dist3 ? mgmt.dist3+sld1.value : 0)/1000).toFixed(2);
} }
Label {
text: "FTM dist4: " + ((mgmt.dist4 ? mgmt.dist4+sld1.value : 0)/1000).toFixed(2);
}
Label {
text: "dist4: " + ((mgmt.dist4 ? mgmt.dist4+sld1.value : 0)/1000).toFixed(2);
}
Slider { Slider {
width: 400; width: 400;
id: sld1; id: sld1;
from: -3000; from: -1000;
to: 5000; to: 1000;
onValueChanged: leCanvas.requestPaint(); value: mgmt.offset;
onValueChanged: {
mgmt.offset = value;
leCanvas.requestPaint();
}
} }
Label { Label {
text: "offset: " + sld1.value; text: "offset: " + sld1.value;
} }
Label {
text: "UWB dist1: " + mgmt.uwbDist1.toFixed(2);
}
Label {
text: "UWB dist2: " + mgmt.uwbDist2.toFixed(2);
}
Label {
text: "UWB dist3: " + mgmt.uwbDist3.toFixed(2);
}
Label {
text: "UWB dist4: " + mgmt.uwbDist4.toFixed(2);
}
Connections { Connections {
target: mgmt target: mgmt
onDistChanged: leCanvas.requestPaint(); onDistChanged: leCanvas.requestPaint();
} }
Button { RowLayout {
text: "party hard"; Button {
onClicked: mgmt.trigger(); text: "party hard";
} onClicked: mgmt.trigger();
}
Button { Button {
text: "stop" text: "stop"
onClicked: mgmt.stop(); onClicked: mgmt.stop();
}
Button {
text: "reset sld"
onClicked: {
sld1.value = 0;
}
}
Button {
text: "High Five"
onClicked: {
mgmt.manualCheckpoint();
}
}
} }
} }
Canvas { Canvas {
@@ -74,14 +112,17 @@ Window {
readonly property double s: 0.02; readonly property double s: 0.02;
function leArc(ctx, cx, cy, dist, stdDev) { function leArc(ctx, cx, cy, dist, stdDev, caption) {
// center circle // center circle
ctx.fillStyle = "#000000"; ctx.fillStyle = (dist < 0 ? "#FF0000" : "#000000");
ctx.beginPath(); ctx.beginPath();
ctx.arc(cx*s, cy*s, 2, 0, 360); ctx.arc(cx*s, cy*s, 2, 0, 360);
ctx.fill(); ctx.fill();
ctx.font = "12px Arial";
ctx.fillText(caption, cx*s, cy*s);
// error circle // error circle
// ctx.beginPath(); // ctx.beginPath();
// ctx.arc(cx*s, cy*s, (dist+sld1.value)*s, 0, 360); // ctx.arc(cx*s, cy*s, (dist+sld1.value)*s, 0, 360);
@@ -91,12 +132,13 @@ Window {
// ctx.stroke(); // ctx.stroke();
// circle // circle
ctx.lineWidth = 1; if (dist > 0) {
ctx.strokeStyle = "#aa000000"; ctx.lineWidth = 1;
ctx.beginPath(); ctx.strokeStyle = "#aa000000";
ctx.arc(cx*s, cy*s, (dist+sld1.value)*s, 0, 360); ctx.beginPath();
ctx.stroke(); ctx.arc(cx*s, cy*s, dist*s, 0, 360);
ctx.stroke();
}
} }
onPaint: { onPaint: {
@@ -110,53 +152,68 @@ Window {
//ctx.strokeStyle = Qt.rgba(0, 0, 0, 1) //ctx.strokeStyle = Qt.rgba(0, 0, 0, 1)
//ctx.lineWidth = 1 //ctx.lineWidth = 1
var ox = 4000; // var ox = 4000;
var oy = 4000; // var oy = 4000;
var cx1 = ox+0; // var cx1 = ox+0;
var cy1 = oy+0; // var cy1 = oy+0;
var cx2 = ox+0; // var cx2 = ox+0;
var cy2 = oy+7250; // var cy2 = oy+7250;
var cx3 = ox+9000; // var cx3 = ox+9000;
var cy3 = oy+7250; // var cy3 = oy+7250;
var cx4 = ox+9000; // var cx4 = ox+9000;
var cy4 = oy+0; // var cy4 = oy+0;
leArc(ctx, cx1, cy1, mgmt.dist1, mgmt.stdDev1); var ox = 4000;
leArc(ctx, cx2, cy2, mgmt.dist2, mgmt.stdDev2); var oy = 4000;
leArc(ctx, cx3, cy3, mgmt.dist3, mgmt.stdDev3);
leArc(ctx, cx4, cy4, mgmt.dist4, mgmt.stdDev4); var cx1 = ox+0;
var cy1 = oy+0;
var cx2 = ox+9000;
var cy2 = oy+0;
var cx3 = ox+9000;
var cy3 = oy+6200;
var cx4 = ox+0;
var cy4 = oy+6200;
leArc(ctx, cx1, cy1, mgmt.dist1, mgmt.stdDev1, 1);
leArc(ctx, cx2, cy2, mgmt.dist2, mgmt.stdDev2, 2);
leArc(ctx, cx3, cy3, mgmt.dist3, mgmt.stdDev3, 3);
leArc(ctx, cx4, cy4, mgmt.dist4, mgmt.stdDev4, 4);
var sigma = 2000; var sigma = 2000;
var stepSize = 333; var stepSize = 333;
var maxP = Math.pow( 1.0 / Math.sqrt(2*Math.PI*sigma), 4.1); var maxP = Math.pow( 1.0 / Math.sqrt(2*Math.PI*sigma), 4.1);
for (var y = 0; y < leCanvas.height/s; y += stepSize) { // for (var y = 0; y < leCanvas.height/s; y += stepSize) {
for (var x = 0; x < leCanvas.width/s; x += stepSize) { // for (var x = 0; x < leCanvas.width/s; x += stepSize) {
var d1 = Math.sqrt( Math.pow(x-cx1, 2) + Math.pow(y-cy1, 2) ) - (mgmt.dist1-sld1.value); // var d1 = Math.sqrt( Math.pow(x-cx1, 2) + Math.pow(y-cy1, 2) ) - (mgmt.dist1-sld1.value);
var d2 = Math.sqrt( Math.pow(x-cx2, 2) + Math.pow(y-cy2, 2) ) - (mgmt.dist2-sld1.value); // var d2 = Math.sqrt( Math.pow(x-cx2, 2) + Math.pow(y-cy2, 2) ) - (mgmt.dist2-sld1.value);
var d3 = Math.sqrt( Math.pow(x-cx3, 2) + Math.pow(y-cy3, 2) ) - (mgmt.dist3-sld1.value); // var d3 = Math.sqrt( Math.pow(x-cx3, 2) + Math.pow(y-cy3, 2) ) - (mgmt.dist3-sld1.value);
var d4 = Math.sqrt( Math.pow(x-cx4, 2) + Math.pow(y-cy4, 2) ) - (mgmt.dist4-sld1.value); // var d4 = Math.sqrt( Math.pow(x-cx4, 2) + Math.pow(y-cy4, 2) ) - (mgmt.dist4-sld1.value);
var p = 1; // var p = 1;
p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d1*d1/(2*sigma*sigma) ); // p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d1*d1/(2*sigma*sigma) );
p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d2*d2/(2*sigma*sigma) ); // p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d2*d2/(2*sigma*sigma) );
p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d3*d3/(2*sigma*sigma) ); // p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d3*d3/(2*sigma*sigma) );
p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d4*d4/(2*sigma*sigma) ); // p *= 1.0 / Math.sqrt(2*Math.PI*sigma) * Math.exp( - d4*d4/(2*sigma*sigma) );
//p = Math.pow(p, 1); // //p = Math.pow(p, 1);
if (p > maxP / 50) { // if (p > maxP / 50) {
ctx.fillStyle = Qt.rgba(1,0,0, p/maxP); // ctx.fillStyle = Qt.rgba(1,0,0, p/maxP);
ctx.fillRect(x*s, y*s, stepSize*s, stepSize*s); // ctx.fillRect(x*s, y*s, stepSize*s, stepSize*s);
} // }
} // }
} // }
} }
} }
} }

99
uwb.h Normal file
View File

@@ -0,0 +1,99 @@
#ifndef UWB_H
#define UWB_H
#include <cstdint>
#include <vector>
struct UwbPosition {
int x = 0;
int y = 0;
int z = 0;
uint8_t quali = 0;
};
struct UwbDistance {
uint16_t nodeID = 0;
int distance = 0;
uint8_t quali = 0;
};
struct UwbResult {
UwbPosition pos;
std::vector<UwbDistance> distances;
};
static void uwb_parse_pos(const unsigned char input[], UwbPosition& pos)
{
// X,Y,Z coordinates (each 4 bytes) and quality factor (1 byte), total size: 13 bytes
pos.x = input[0];
pos.x |= input[1] << 8;
pos.x |= input[2] << 16;
pos.x |= input[3] << 24;
pos.y = input[4];
pos.y |= input[5] << 8;
pos.y |= input[6] << 16;
pos.y |= input[7] << 24;
pos.z = input[8];
pos.z |= input[9] << 8;
pos.z |= input[10] << 16;
pos.z |= input[11] << 24;
pos.quali = input[12];
}
static void uwb_parse_dist(const unsigned char input[], std::vector<UwbDistance>& result)
{
// First byte is distance count(1 byte)
// Sequence of node ID(2 bytes), distance in mm(4 bytes) and quality factor(1 byte)
// Max value contains 15 elements, size: 8 - 106
size_t numOfAnchors = input[0];
for (size_t i = 0; i < numOfAnchors; i++)
{
size_t offset = 1 + i * 7;
UwbDistance dist;
dist.nodeID = input[offset + 1] << 8 | input[offset];
dist.distance = input[offset + 5] << 24
| input[offset + 4] << 16
| input[offset + 3] << 8
| input[offset + 2];
dist.quali = input[offset + 6];
result.push_back(dist);
}
}
static UwbResult uwb_parse(const unsigned char input[])
{
UwbResult result;
if (input[0] == 0)
{
// pos only
uwb_parse_pos(&input[1], result.pos);
}
else if (input[0] == 1)
{
// dist only
uwb_parse_dist(&input[1], result.distances);
}
else if (input[0] == 2)
{
// Encoded Position (as above, 13 bytes)
// Encoded Distances(as above, 8 - 29 bytes).Position and distances
// are sent by tag, with a maximum number of 4 ranging anchors
uwb_parse_pos(&input[1], result.pos);
uwb_parse_dist(&input[1 + 13], result.distances);
}
return result;
}
#endif // UWB_H