From 2608ca37d9c0f717a2ded3393833b1cbc1846745 Mon Sep 17 00:00:00 2001 From: Peter Hasse Date: Fri, 28 Mar 2025 13:54:09 +0100 Subject: [PATCH 01/35] update gradle to 8.9.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d497b5b1..a0e9d158 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.9.0' + classpath 'com.android.tools.build:gradle:8.9.1' def nav_version = "2.5.3" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21' From c9d68c4a5dca9b85bdb71c1232bee74c7c1bfad3 Mon Sep 17 00:00:00 2001 From: Peter Hasse Date: Fri, 28 Mar 2025 13:56:36 +0100 Subject: [PATCH 02/35] udpate version number to 0.6 / 6 --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4553dcb2..a44d92a5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,8 +55,8 @@ android { applicationId "de.fraunhofer.fokus.OpenMobileNetworkToolkit" minSdk 31 targetSdk 36 - versionCode 5 - versionName "0.5" + versionCode 6 + versionName "0.6" resValue("string", "git_hash", getGitHash()) testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -109,7 +109,7 @@ spdxSbom { configurations = ['releaseRuntimeClasspath'] scm { uri.set("https://github.com/omnt/OpenMobileNetworkToolkit") - revision.set("0.5") + revision.set("0.6") } document { name.set("OpenMobileNetworkToolkit") From 657198cfe8be8851735d83d93880282192d137cc Mon Sep 17 00:00:00 2001 From: Johann <9853102+hajoha@users.noreply.github.com> Date: Wed, 2 Apr 2025 16:56:09 +0200 Subject: [PATCH 03/35] fix FEATURE_TELEPHONY_CDMA crash (#51) --- .../OpenMobileNetworkToolkit/DataProvider/DataProvider.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java index 7bfd06c2..2181a7c7 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java @@ -103,6 +103,7 @@ public class DataProvider extends TelephonyCallback implements LocationListener, private ArrayList ssi = new ArrayList<>(); private WifiInformation wi = null; private LocationManager lm; + private PackageManager pm; private final BuildInformation buildInformation = new BuildInformation(); // Time stamp, should be updated on each update of internal data caches private long ts = System.currentTimeMillis(); @@ -113,7 +114,7 @@ public DataProvider(Context context) { ct = context; spg = SharedPreferencesGrouper.getInstance(ct); permission_phone_state = gv.isPermission_phone_state(); - + pm = ct.getPackageManager(); // we can only relay on some APIs if this is a phone. if (gv.isFeature_telephony()) { cm = (ConnectivityManager) ct.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -243,7 +244,7 @@ public void refreshDeviceInformation() { if (tm.hasCarrierPrivileges()) { try { di.setIMEI(tm.getImei()); - di.setMEID(tm.getMeid()); + if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) di.setMEID(tm.getMeid()); di.setSimSerial(tm.getSimSerialNumber()); di.setSubscriberId(tm.getSubscriberId()); di.setNetworkAccessIdentifier(tm.getNai()); From f04fb7e9b657e15b71d1481c6be86692f1630ef8 Mon Sep 17 00:00:00 2001 From: Peter Hasse Date: Thu, 24 Apr 2025 14:12:21 +0200 Subject: [PATCH 04/35] add explanation on active and passive measurements raised in https://github.com/omnt/OpenMobileNetworkToolkit/issues/53#issuecomment-2820910533 --- .../Ping/PingService.java | 15 ++++++++++++++- docs/home.md | 10 +++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java index aa895bcc..8f9f1f6e 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java @@ -6,6 +6,7 @@ import android.icu.text.SimpleDateFormat; import android.os.Environment; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.util.Log; @@ -48,6 +49,7 @@ public class PingService extends Service { private static final String TAG = "PingService"; private FileOutputStream ping_stream; private Handler pingLogging; + private HandlerThread pingLoggingHandleThread; private WorkManager wm; private Context context; private ArrayList pingWRs; @@ -128,7 +130,9 @@ private void setupPing(){ return; } spg.getSharedPreference(SPType.ping_sp).edit().putBoolean("ping", true).apply(); - pingLogging = new Handler(Objects.requireNonNull(Looper.myLooper())); + pingLoggingHandleThread = new HandlerThread("PingLoggingHandlerThread"); + pingLoggingHandleThread.start(); + pingLogging = new Handler(Objects.requireNonNull(pingLoggingHandleThread.getLooper())); PingParser pingParser = PingParser.getInstance(null); propertyChangeListener = pingParser.getListener(); if(propertyChangeListener != null){ @@ -230,6 +234,15 @@ public void onChanged(Object o) { private void stopPing(){ if (pingLogging != null )pingLogging.removeCallbacks(pingUpdate); + if (pingLoggingHandleThread != null) { + pingLoggingHandleThread.quitSafely(); + try { + pingLoggingHandleThread.join(); + } catch (InterruptedException e) { + Log.e(TAG, "Exception happened!! "+e, e); + } + pingLoggingHandleThread = null; + } try { if (ping_stream != null) ping_stream.close(); } catch (IOException e) { diff --git a/docs/home.md b/docs/home.md index dfffedd7..75b1bda7 100644 --- a/docs/home.md +++ b/docs/home.md @@ -1,9 +1,17 @@ # Home Screen -When starting the OMNT or touching the OMNT logo in the top left corner the home screen is displayed. +When starting the OMNT or touching the OMNT logo in the top left corner the ```home screen``` is displayed. +The ```home screen``` provides two tabs, the ```quick view``` and the ```detail view``` +The ```quick view``` presents the most relevant network parameters and is automatically updated. +On the other hand the ```detail view``` is only updated on load or refresh to make it more convenient to read. Here are all information the OMNT can collect displayed. Different cards grouped the data into categories. The data displayed is updates on load and swipe down. This way the data of the time of refresh can be analyzed. The same grouping is applied to the data send to the InfluxDB. +>[!NOTE] +>All information listed below are passive measurements. This means these are information provided by the Android framework collected from +>different source e.g. the mobile base band. E.g. bandwidth in the cell information card is not active measured value but reported by the api. +>For active measurements see [Iperf3](iperf3.md) and [Ping](ping.md). + ## Cell Information This card displays information from the Android [CellInfo API](https://developer.android.com/reference/android/telephony/CellInfo). The cell information are on the cell the phone is camping on or registered to. If neighbor cells are present / announced and display of neighbor cells is enabled in the settings they are displayed to. Information on neighbor cells are usually limited. From a38f9db2dec15f1bf5ee880fe9b2bf979b6a3de1 Mon Sep 17 00:00:00 2001 From: Johann <9853102+hajoha@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:01:37 +0200 Subject: [PATCH 05/35] Add MQTT-Support (#46) * add dep for MQTT lib * add basic setup for MQTT config * add basic infrastructure for MQTT * remove double setupLocalFile() call * update MQTTService * add Service Type * receive last will exactly once * fix typo * move device name setting to main Setting preference and force user to set it before using Logging/MQTT * remove UUID generation * update LoggingFragment * fix monitoring of status * get basic iPerf3 working * add basic progressbar * add basic progressbar * update progressbar based on current interval from iperf3 --- app/build.gradle | 21 +- app/proguard-rules.pro | 7 +- app/src/main/AndroidManifest.xml | 71 +- .../OpenMobileNetworkToolkit/Application.java | 15 + .../CustomEventListener.java | 11 + .../CellInformations/NRInformation.java | 2 +- .../DataProvider/DataProvider.java | 4 + .../Worker/InfluxDB2xUploadWorker.java | 88 + .../Inputs/Inputs.java | 133 ++ .../Inputs/Iperf3Input.java | 191 ++ .../Inputs/PingInput.java | 105 + .../Converter/Iperf3ErrorConverter.java | 30 + .../Converter}/Iperf3InputConverter.java | 11 +- .../Converter/Iperf3IntervalsConverter.java | 85 + .../Converter/Iperf3StartConverter.java | 41 + .../Database/Converter/MetricConverter.java | 36 + .../ArrayListIntervalsAdapter.java | 24 + .../MoshiAdapter/ArrayListStreamsAdapter.java | 29 + .../RunResult}/Iperf3ResultsDataBase.java | 17 +- .../Database/RunResult/Iperf3RunResult.java | 70 + .../RunResult/Iperf3RunResultDao.java | 88 + .../Iperf3/Fragments/Iperf3Fragment.java | 513 +++++ .../Iperf3/Intervals.java | 4 + .../Iperf3/Iperf3Executor.java | 78 + .../Iperf3/Iperf3Fragment.java | 857 -------- .../Iperf3/Iperf3LibLoader.java | 8 +- .../Iperf3/Iperf3ListFragment.java | 151 -- .../Iperf3/Iperf3LogFragment.java | 367 ---- .../Iperf3/Iperf3Parser.java | 88 - .../Iperf3/Iperf3RecyclerViewAdapter.java | 420 ++-- .../Iperf3/Iperf3RunResult.java | 51 - .../Iperf3/Iperf3RunResultDao.java | 52 - .../Iperf3/Iperf3Worker.java | 121 -- .../Iperf3/JSON/Interval/Interval.java | 37 +- .../Iperf3/JSON/Interval/Streams/Streams.java | 5 + .../JSON/Interval/Streams/UDP/UDP_STREAM.java | 1 - .../Iperf3/JSON/start/Start.java | 6 + .../Executor/Iperf3ServiceWorkerFour.java | 7 + .../Executor/Iperf3ServiceWorkerOne.java | 7 + .../Executor/Iperf3ServiceWorkerThree.java | 7 + .../Executor/Iperf3ServiceWorkerTwo.java | 7 + .../Iperf3MonitorServiceWorkerFour.java | 7 + .../Iperf3MonitorServiceWorkerOne.java | 7 + .../Iperf3MonitorServiceWorkerThree.java | 7 + .../Iperf3MonitorServiceWorkerTwo.java | 7 + .../Iperf3/Worker/Iperf3ExecutorWorker.java | 152 ++ .../Iperf3/Worker/Iperf3MonitorWorker.java | 308 +++ .../Iperf3ToLineProtocolWorker.java | 189 +- .../{ => Worker}/Iperf3UploadWorker.java | 20 +- .../LoggingService.java | 6 +- .../MQTT/Handler/Handler.java | 41 + .../MQTT/Handler/Iperf3Handler.java | 174 ++ .../MQTT/Handler/PingHandler.java | 116 ++ .../MQTT/MQTTService.java | 506 +++++ .../MainActivity.java | 25 +- .../Metric/Metric.java | 240 --- .../Metric/MetricCalculator.java | 119 ++ .../Metric/MetricView.java | 169 ++ .../Parameter/Iperf3Parameter.java | 1810 +++++++++++++++++ .../Parameter/Parameter.java | 56 + .../Parameter/PingParameter.java | 182 ++ .../Ping/PingFragment.java | 31 +- .../Ping/PingInformations/PacketLossLine.java | 7 +- .../Ping/PingParser.java | 8 +- .../Ping/PingService.java | 38 +- .../Ping/PingWorker.java | 210 -- .../Ping/Worker/PingToLineProtocolWorker.java | 141 ++ .../Ping/Worker/PingWorker.java | 165 ++ .../Preferences/SPType.java | 7 +- .../Preferences/SharedPreferencesGrouper.java | 7 +- .../RemoteWorkInfoChecker.java | 97 + .../LoggingSettingsFragment.java | 66 +- .../MQTTSettingsFragment.java | 44 + app/src/main/res/drawable/mqtt.xml | 5 + .../main/res/layout/fragment_iperf3_input.xml | 556 +++-- .../main/res/layout/fragment_iperf3_list.xml | 41 - .../res/layout/fragment_iperf3_row_item.xml | 84 +- .../main/res/layout/iperf3_notification.xml | 40 + app/src/main/res/layout/parameter_view.xml | 31 + app/src/main/res/navigation/nav_graph.xml | 23 +- app/src/main/res/values/strings.xml | 28 +- app/src/main/res/values/styles.xml | 59 +- app/src/main/res/values/themes.xml | 2 + app/src/main/res/xml/preference.xml | 28 +- app/src/main/res/xml/preference_logging.xml | 3 - app/src/main/res/xml/preference_mqtt.xml | 63 + build.gradle | 1 + gradle.properties | 2 +- settings.gradle | 1 + 89 files changed, 6941 insertions(+), 2854 deletions(-) create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Application.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/CustomEventListener.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/InfluxDB2x/Worker/InfluxDB2xUploadWorker.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Inputs.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Iperf3Input.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/PingInput.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3ErrorConverter.java rename app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/{ => Database/Converter}/Iperf3InputConverter.java (61%) create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3IntervalsConverter.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3StartConverter.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/MetricConverter.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListIntervalsAdapter.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListStreamsAdapter.java rename app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/{ => Database/RunResult}/Iperf3ResultsDataBase.java (54%) create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResult.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResultDao.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Fragments/Iperf3Fragment.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Executor.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Fragment.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ListFragment.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LogFragment.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Parser.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResult.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResultDao.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Worker.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerFour.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerOne.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerThree.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerTwo.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerFour.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerOne.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerThree.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerTwo.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3ExecutorWorker.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3MonitorWorker.java rename app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/{ => Worker}/Iperf3ToLineProtocolWorker.java (53%) rename app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/{ => Worker}/Iperf3UploadWorker.java (78%) create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Handler.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Iperf3Handler.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/PingHandler.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/MQTTService.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/Metric.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricCalculator.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricView.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Iperf3Parameter.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Parameter.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/PingParameter.java delete mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingWorker.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingToLineProtocolWorker.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingWorker.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/RemoteWorkInfoChecker.java create mode 100644 app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/MQTTSettingsFragment.java create mode 100644 app/src/main/res/drawable/mqtt.xml delete mode 100644 app/src/main/res/layout/fragment_iperf3_list.xml create mode 100644 app/src/main/res/layout/iperf3_notification.xml create mode 100644 app/src/main/res/layout/parameter_view.xml create mode 100644 app/src/main/res/xml/preference_mqtt.xml diff --git a/app/build.gradle b/app/build.gradle index a44d92a5..a745b749 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,6 +34,11 @@ def getGitHash = { -> } android { + packagingOptions { + resources { + excludes += ['META-INF/INDEX.LIST', 'META-INF/io.netty.versions.properties'] + } + } signingConfigs { debug { keyAlias keystoreProperties['keyAlias'] @@ -122,10 +127,18 @@ spdxSbom { } dependencies { - implementation 'androidx.preference:preference:1.2.1' - implementation 'androidx.work:work-runtime:2.10.0' + def work_version = "2.10.0" def room_version = "2.6.1" + implementation "androidx.work:work-runtime:$work_version" + implementation "androidx.work:work-runtime-ktx:$work_version" + implementation "androidx.work:work-rxjava2:$work_version" + implementation "androidx.work:work-gcm:$work_version" + androidTestImplementation "androidx.work:work-testing:$work_version" + implementation "androidx.work:work-multiprocess:$work_version" + implementation 'com.google.android.flexbox:flexbox:3.0.0' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0" + implementation 'androidx.preference:preference:1.2.1' implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version" implementation 'androidx.appcompat:appcompat:1.7.0' @@ -149,7 +162,11 @@ dependencies { implementation 'com.google.android.gms:play-services-location:21.3.0' implementation 'com.github.anastr:speedviewlib:1.6.1' implementation "androidx.viewpager2:viewpager2:1.1.0" + implementation "androidx.compose.material3:material3:1.3.0" + implementation("com.hivemq:hivemq-mqtt-client:1.3.4") implementation "androidx.compose.material3:material3:1.3.1" + implementation("com.squareup.moshi:moshi:1.15.2") + implementation("com.squareup.moshi:moshi-adapters:1.8.0") } configurations.implementation { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 9c325e21..09374eb0 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -29,4 +29,9 @@ -assumenosideeffects class android.util.Log { public static *** d(...); public static *** v(...); -} \ No newline at end of file +} + +-keepclassmembernames class io.netty.** { *; } +-keepclassmembers class org.jctools.** { *; } + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4dddc0e6..b9fb0ab9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,8 +38,8 @@ - + + + + + + + + + + + + + + + + + + + + + + + + @@ -128,6 +196,5 @@ - \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Application.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Application.java new file mode 100644 index 00000000..c392e698 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Application.java @@ -0,0 +1,15 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit; + +import androidx.annotation.NonNull; +import androidx.work.Configuration; + +public class Application extends android.app.Application implements Configuration.Provider { + @NonNull + @Override + public Configuration getWorkManagerConfiguration() { + return new Configuration.Builder() + .setDefaultProcessName(getPackageName()) + .setMinimumLoggingLevel(android.util.Log.DEBUG) + .build(); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/CustomEventListener.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/CustomEventListener.java new file mode 100644 index 00000000..afff4596 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/CustomEventListener.java @@ -0,0 +1,11 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit; + +import androidx.work.WorkInfo; + +import java.util.EventListener; +import java.util.HashMap; +import java.util.UUID; + +public interface CustomEventListener extends EventListener { + void onChange(HashMap workInfos); +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/CellInformations/NRInformation.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/CellInformations/NRInformation.java index 110706f7..97825ba1 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/CellInformations/NRInformation.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/CellInformations/NRInformation.java @@ -297,7 +297,7 @@ public StringBuilder getStringBuilder(){ if(!this.getSsrsrpString().equals(max)) stringBuilder.append(" SSRSRP: ").append(this.getSsrsrpString()).append(" dBm").append("\n"); - if(!this.getSssinrString().equals(max)) stringBuilder.append(" SSRSRP: ").append(this.getSssinrString()).append(" dBm").append("\n"); + if(!this.getSssinrString().equals(max)) stringBuilder.append(" SSSINR: ").append(this.getSssinrString()).append(" dBm").append("\n"); if(!this.getFirstCqiString().equals(max)) stringBuilder.append(" CQI: ").append(this.getFirstCqiString()).append("\n"); diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java index 2181a7c7..0b5e60d9 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java @@ -70,6 +70,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.UUID; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.DataProvider.CellInformations.CDMAInformation; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.DataProvider.CellInformations.CellInformation; @@ -731,6 +732,9 @@ public Map getTagsMap() { tags_map_modifiable.put("sdk_version", String.valueOf(di.getAndroidSDK())); tags_map_modifiable.put("android_version", di.getAndroidRelease()); tags_map_modifiable.put("security_patch", di.getSecurityPatchLevel()); + String device = spg.getSharedPreference(SPType.default_sp).getString("device_name", "null").strip(); + //if(device.equals("null")); TODO handle this + tags_map_modifiable.put("device", device); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { tags_map_modifiable.put("soc_model", di.getSOCModel()); } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/InfluxDB2x/Worker/InfluxDB2xUploadWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/InfluxDB2x/Worker/InfluxDB2xUploadWorker.java new file mode 100644 index 00000000..55115321 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/InfluxDB2x/Worker/InfluxDB2xUploadWorker.java @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.Worker; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.Data; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.google.gson.Gson; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.InfluxdbConnection; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.InfluxdbConnections; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Inputs; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; + +public class InfluxDB2xUploadWorker extends Worker { + public static final String TAG = "InfDB2xUploadWorker"; + InfluxdbConnection influx; + private Inputs input; + public static final String UPLOAD = "influxdb2x_upload"; + + public InfluxDB2xUploadWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + Gson gson = new Gson(); + String inputString = getInputData().getString(Inputs.INPUT); + input = gson.fromJson(inputString, Inputs.class); + } + private void setup(){ + influx = InfluxdbConnections.getRicInstance(getApplicationContext()); + } + + @NonNull + @Override + public Result doWork() { + setup(); + Data output = new Data.Builder().putBoolean(UPLOAD, false).build(); + if(influx == null){ + return Result.failure(output); + } + if(influx.getWriteApi() == null){ + influx.open_write_api(); + if(influx.getWriteApi() == null) + return Result.failure(output); + } + + if(!influx.ping()){ + return Result.failure(output); + } + BufferedReader br; + try { + br = new BufferedReader(new FileReader(input.getParameter().getLineProtocolFile())); + } catch (FileNotFoundException | NullPointerException e) { + Log.d(TAG,e.toString()); + return Result.failure(output); + } + List points = br.lines().collect(Collectors.toList()); + try { + Log.d(TAG, String.format("doWork: uploading %s", input.getParameter().getLineProtocolFile())); + influx.writeRecords(points); + } catch (IOException e) { + Log.d(TAG, String.format("doWork: upload of %s failed!", input.getParameter().getLineProtocolFile())); + return Result.failure(output); + } + + + influx.flush(); + + output = new Data.Builder().putBoolean(UPLOAD, true).build(); + return Result.success(output); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Inputs.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Inputs.java new file mode 100644 index 00000000..8855ffb1 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Inputs.java @@ -0,0 +1,133 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs; + +import android.os.Environment; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.work.Data; +import androidx.work.OneTimeWorkRequest; + +import java.sql.Timestamp; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.Parameter; + +public class Inputs implements Parcelable { + + + public static final String INPUT = "input"; + public static final String TESTUUID = "testUUID"; + public static final String SEQUENCEUUID = "sequenceUUID"; + public static final String MEASUREMENTUUID = "measurementUUID"; + public static final String CAMPAIGNUUID = "campaignUUID"; + public static final String DEVICE = "device"; + public static final String TYPE = "type"; + public static final String PARAMS = "params"; + public static final String NOTIFICATIONUMBER = "notificationNumber"; + private String testUUID; + + private Timestamp timestamp; + private String campaignUUID; + private String sequenceUUID; + private String measurementUUID; + private Parameter parameter; + + + public static final Creator CREATOR = new Creator() { + @Override + public Inputs createFromParcel(Parcel in) { + return new Inputs(in); + } + + @Override + public Inputs[] newArray(int size) { + return new Inputs[size]; + } + }; + + public String getTestUUID() { + return testUUID; + } + + public void setTestUUID(String testUUID) { + this.testUUID = testUUID; + } + + + public Timestamp getTimestamp() { + return timestamp; + } + + public void setTimestamp(Timestamp timestamp) { + this.timestamp = timestamp; + } + + public String getCampaignUUID() { + return campaignUUID; + } + + public String getSequenceUUID() { + return sequenceUUID; + } + + public String getMeasurementUUID() { + return measurementUUID; + } + protected Inputs(Parcel in) { + timestamp = (Timestamp) in.readSerializable(); + campaignUUID = in.readString(); + sequenceUUID = in.readString(); + measurementUUID = in.readString(); + testUUID = in.readString(); + parameter = in.readParcelable(Parameter.class.getClassLoader()); + } + + public Inputs(String campaignUUID, String sequenceUUID, String measurementUUID, String testUUID, Parameter parameter) { + this.timestamp = new Timestamp(System.currentTimeMillis()); + this.campaignUUID = campaignUUID; + this.sequenceUUID = sequenceUUID; + this.measurementUUID = measurementUUID; + this.testUUID = testUUID; + this.parameter = parameter; + } + + public Parameter getParameter() { + return parameter; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeSerializable(timestamp); + dest.writeString(campaignUUID); + dest.writeString(sequenceUUID); + dest.writeString(measurementUUID); + dest.writeString(testUUID); + dest.writeParcelable(parameter, flags); + } + + public Data.Builder getInputAsDataBuilder(int i, String packageName) { + return null; + } + + public OneTimeWorkRequest getWorkRequestExecutor(int i, String packageName) { + return null; + } + + public OneTimeWorkRequest getWorkRequestMonitor(int i, String packageName) { + return null; + } + + public OneTimeWorkRequest getWorkRequestLineProtocol(int i, String packageName) { + return null; + } + + public OneTimeWorkRequest getWorkRequestUpload(int i, String packageName) { + return null; + } + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Iperf3Input.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Iperf3Input.java new file mode 100644 index 00000000..875a2a85 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/Iperf3Input.java @@ -0,0 +1,191 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs; + +import static androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_CLASS_NAME; +import static androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_PACKAGE_NAME; + +import android.content.ComponentName; +import android.os.Parcel; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.Data; +import androidx.work.OneTimeWorkRequest; + + +import com.google.gson.GsonBuilder; + + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor.Iperf3MonitorServiceWorkerFour; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor.Iperf3MonitorServiceWorkerOne; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor.Iperf3MonitorServiceWorkerThree; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor.Iperf3MonitorServiceWorkerTwo; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3MonitorWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.Iperf3Parameter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor.Iperf3ServiceWorkerFour; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor.Iperf3ServiceWorkerThree; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor.Iperf3ServiceWorkerTwo; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3ExecutorWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor.Iperf3ServiceWorkerOne; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3ToLineProtocolWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3UploadWorker; + + +public class Iperf3Input extends Inputs { + + private static final String TAG = "Iperf3Input"; + public static final String IPERF3UUID = "iPerf3UUID"; + + private Iperf3Parameter iperf3Parameter; + protected Iperf3Input(Parcel in) { + super(in); + iperf3Parameter = in.readParcelable(Iperf3Parameter.class.getClassLoader()); + } + + + public static final Creator CREATOR = new Creator<>() { + @Override + public Iperf3Input createFromParcel(Parcel in) { + return new Iperf3Input(in); + } + + @Override + public Iperf3Input[] newArray(int size) { + return new Iperf3Input[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int i) { + super.writeToParcel(parcel, i); +// parcel.writeParcelable(iperf3Parameter, i); + } + public Iperf3Input(Iperf3Parameter iperf3Parameter, String testUUID) { + this(iperf3Parameter, testUUID, "", "", ""); + } + public Iperf3Input(Iperf3Parameter iperf3Parameter, + String testUUID, + String sequenceUUID, + String measurementUUID, + String campaignUUID) { + super(testUUID, sequenceUUID, measurementUUID, campaignUUID, iperf3Parameter); + this.iperf3Parameter = iperf3Parameter; + } + + @Override + public Iperf3Parameter getParameter() { + return this.iperf3Parameter; + } + + + public Data.Builder getInputAsDataBuilder(int workerProcess, String packageName, String serviceName) { + Data.Builder data = new Data.Builder(); + ComponentName componentName = new ComponentName(packageName, serviceName); + data.putString(ARGUMENT_PACKAGE_NAME, componentName.getPackageName()); + data.putString(ARGUMENT_CLASS_NAME, componentName.getClassName()); + data.putInt(NOTIFICATIONUMBER, workerProcess); + data.putString(Inputs.INPUT, new GsonBuilder().create().toJson(this, Iperf3Input.class)); + Log.d(TAG, "getInputAsDataBuilder: ARGUMENT_PACKAGE_NAME: " + componentName.getPackageName()); + Log.d(TAG, "getInputAsDataBuilder: ARGUMENT_CLASS_NAME: " + componentName.getClassName()); + Log.d(TAG, "getInputAsDataBuilder: workerProcess: " + workerProcess); + Log.d(TAG, "getInputAsDataBuilder: testUUID: " + getTestUUID()); + return data; + } + + + + public Data.Builder getInputAsDataBuilder(int i) { + Data.Builder data = new Data.Builder(); + data.putInt(NOTIFICATIONUMBER, i); + data.putString(Inputs.INPUT, new GsonBuilder().create().toJson(this, Iperf3Input.class)); + return data; + } + + @Override + public OneTimeWorkRequest getWorkRequestExecutor(int i, String packageName) { + + String serviceName = ""; + switch (i){ + case 0: + serviceName = Iperf3ServiceWorkerOne.class.getName(); + break; + case 1: + serviceName = Iperf3ServiceWorkerTwo.class.getName(); + break; + case 2: + serviceName = Iperf3ServiceWorkerThree.class.getName(); + break; + case 3: + serviceName = Iperf3ServiceWorkerFour.class.getName(); + break; + } + + return new OneTimeWorkRequest.Builder(Iperf3ExecutorWorker.class) + .addTag(super.getTestUUID()) + .addTag(super.getMeasurementUUID()) + .addTag(super.getSequenceUUID()) + .addTag(super.getCampaignUUID()) + .addTag(Iperf3ExecutorWorker.TAG) + .addTag(getParameter().getiPerf3UUID()) + .setInputData(getInputAsDataBuilder(i, packageName, serviceName).build()) + .build(); + } + + @Override + public OneTimeWorkRequest getWorkRequestMonitor(int i, String packageName) { + String serviceName = ""; + switch (i){ + case 0: + serviceName = Iperf3MonitorServiceWorkerOne.class.getName(); + break; + case 1: + serviceName = Iperf3MonitorServiceWorkerTwo.class.getName(); + break; + case 2: + serviceName = Iperf3MonitorServiceWorkerThree.class.getName(); + break; + case 3: + serviceName = Iperf3MonitorServiceWorkerFour.class.getName(); + break; + } + return new OneTimeWorkRequest.Builder(Iperf3MonitorWorker.class) + .addTag(super.getTestUUID()) + .addTag(super.getMeasurementUUID()) + .addTag(super.getSequenceUUID()) + .addTag(super.getCampaignUUID()) + .addTag(Iperf3MonitorWorker.TAG) + .addTag(getParameter().getiPerf3UUID()) + .setInputData(getInputAsDataBuilder(i, packageName, serviceName).build()) + .build(); + } + + @Override + public OneTimeWorkRequest getWorkRequestLineProtocol(int i, String packageName) { + return new OneTimeWorkRequest.Builder(Iperf3ToLineProtocolWorker.class) + .addTag(super.getTestUUID()) + .addTag(super.getMeasurementUUID()) + .addTag(super.getSequenceUUID()) + .addTag(super.getCampaignUUID()) + .addTag(Iperf3ToLineProtocolWorker.TAG) + .addTag(getParameter().getiPerf3UUID()) + .setInputData(getInputAsDataBuilder(i).build()) + .build(); + } + + @Override + public OneTimeWorkRequest getWorkRequestUpload(int i, String packageName) { + return new OneTimeWorkRequest.Builder(Iperf3UploadWorker.class) + .addTag(super.getTestUUID()) + .addTag(super.getMeasurementUUID()) + .addTag(super.getSequenceUUID()) + .addTag(super.getCampaignUUID()) + .addTag(Iperf3UploadWorker.TAG) + .addTag(getParameter().getiPerf3UUID()) + .setInputData(getInputAsDataBuilder(i).build()) + .build(); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/PingInput.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/PingInput.java new file mode 100644 index 00000000..cb37252e --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Inputs/PingInput.java @@ -0,0 +1,105 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs; + +import android.os.Parcel; + +import androidx.work.Data; +import androidx.work.OneTimeWorkRequest; + +import com.google.gson.Gson; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.Worker.InfluxDB2xUploadWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3ToLineProtocolWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.PingParameter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.Worker.PingToLineProtocolWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.Worker.PingWorker; + + +public class PingInput extends Inputs { + private PingParameter pingParameter; + + protected PingInput(Parcel in) { + super(in); + + } + public PingInput(PingParameter pingParameter, + String testUUID, + String sequenceUUID, + String measurementUUID, + String campaignUUID) { + super(testUUID, sequenceUUID, measurementUUID, campaignUUID, pingParameter); + this.pingParameter = pingParameter; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + @Override + public Data.Builder getInputAsDataBuilder(int i, String packageName) { + Data.Builder data = new Data.Builder(); + Gson gson = new Gson(); + data.putInt(NOTIFICATIONUMBER, i); + data.putString(INPUT, gson.toJson(this)); + return data; + } + + public PingParameter getPingParameter() { + return pingParameter; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public PingInput createFromParcel(Parcel in) { + return new PingInput(in); + } + + @Override + public PingInput[] newArray(int size) { + return new PingInput[size]; + } + }; + + + @Override + public OneTimeWorkRequest getWorkRequestLineProtocol(int i, String packageName) { + // TODO FIX + return new OneTimeWorkRequest.Builder(PingToLineProtocolWorker.class) + .addTag(super.getTestUUID()) + .addTag(super.getMeasurementUUID()) + .addTag(super.getSequenceUUID()) + .addTag(super.getCampaignUUID()) + .addTag(PingToLineProtocolWorker.TAG) // TODO FIX + .setInputData(getInputAsDataBuilder(i, packageName).build()) + .build(); + } + + @Override + public OneTimeWorkRequest getWorkRequestUpload(int i, String packageName) { + return new OneTimeWorkRequest.Builder(InfluxDB2xUploadWorker.class) + .addTag(super.getTestUUID()) + .addTag(super.getMeasurementUUID()) + .addTag(super.getSequenceUUID()) + .addTag(super.getCampaignUUID()) + .addTag(InfluxDB2xUploadWorker.TAG) + .setInputData(getInputAsDataBuilder(i, packageName).build()) + .build(); + } + + @Override + public OneTimeWorkRequest getWorkRequestExecutor(int i, String packageName) { + return new OneTimeWorkRequest.Builder(PingWorker.class) + .addTag(super.getTestUUID()) + .addTag(super.getMeasurementUUID()) + .addTag(super.getSequenceUUID()) + .addTag(super.getCampaignUUID()) + .addTag(PingWorker.TAG) + .setInputData(getInputAsDataBuilder(i, packageName).build()) + .build(); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3ErrorConverter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3ErrorConverter.java new file mode 100644 index 00000000..0b9e5f89 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3ErrorConverter.java @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter; + +import androidx.room.ProvidedTypeConverter; +import androidx.room.TypeConverter; + +import com.google.gson.Gson; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Error; + + +@ProvidedTypeConverter +public class Iperf3ErrorConverter { + @TypeConverter + public Error fromJSONString(String string) { + return new Gson().fromJson(string, Error.class); + } + + @TypeConverter + public String toJSONString(Error example) { + return new Gson().toJson(example); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3InputConverter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3InputConverter.java similarity index 61% rename from app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3InputConverter.java rename to app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3InputConverter.java index 61c014c5..2820800d 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3InputConverter.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3InputConverter.java @@ -6,22 +6,25 @@ * SPDX-License-Identifier: BSD-3-Clause-Clear */ -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter; import androidx.room.ProvidedTypeConverter; import androidx.room.TypeConverter; import com.google.gson.Gson; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; + + @ProvidedTypeConverter public class Iperf3InputConverter { @TypeConverter - public Iperf3Fragment.Iperf3Input StringToIperf3Input(String string) { - return new Gson().fromJson(string, Iperf3Fragment.Iperf3Input.class); + public Iperf3Input StringToIperf3Input(String string) { + return new Gson().fromJson(string, Iperf3Input.class); } @TypeConverter - public String Iperf3InputToString(Iperf3Fragment.Iperf3Input example) { + public String Iperf3InputToString(Iperf3Input example) { return new Gson().toJson(example); } } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3IntervalsConverter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3IntervalsConverter.java new file mode 100644 index 00000000..ab8e4893 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3IntervalsConverter.java @@ -0,0 +1,85 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter; + +import androidx.room.ProvidedTypeConverter; +import androidx.room.TypeConverter; + +import com.google.common.reflect.TypeToken; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; +import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.List; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.MoshiAdapter.ArrayListIntervalsAdapter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.MoshiAdapter.ArrayListStreamsAdapter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Intervals; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.STREAM_TYPE; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.Stream; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.Streams; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.TCP.TCP_DL_STREAM; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.TCP.TCP_UL_STREAM; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.UDP.UDP_DL_STREAM; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.UDP.UDP_UL_STREAM; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.SUM_TYPE; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.Sum; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.TCP.TCP_DL_SUM; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.TCP.TCP_UL_SUM; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.UDP.UDP_DL_SUM; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.UDP.UDP_UL_SUM; + +@ProvidedTypeConverter +public class Iperf3IntervalsConverter { + + + @TypeConverter + public Intervals stringToIperf3Intervals(String string) { + Moshi moshi = new Moshi.Builder() + .add(new ArrayListIntervalsAdapter()) + .add(new ArrayListStreamsAdapter()) + + .add(PolymorphicJsonAdapterFactory.of(Stream.class, "streamType") + .withSubtype(TCP_DL_STREAM.class, STREAM_TYPE.TCP_DL.toString()) + .withSubtype(TCP_UL_STREAM.class, STREAM_TYPE.TCP_UL.toString()) + .withSubtype(UDP_DL_STREAM.class, STREAM_TYPE.UDP_DL.toString()) + .withSubtype(UDP_UL_STREAM.class, STREAM_TYPE.UDP_UL.toString())) + .add(PolymorphicJsonAdapterFactory.of(Sum.class, "sumType") + .withSubtype(TCP_DL_SUM.class, SUM_TYPE.TCP_DL.toString()) + .withSubtype(TCP_UL_SUM.class, SUM_TYPE.TCP_UL.toString()) + .withSubtype(UDP_DL_SUM.class, SUM_TYPE.UDP_DL.toString()) + .withSubtype(UDP_UL_SUM.class, SUM_TYPE.UDP_UL.toString())) + .build(); + JsonAdapter jsonAdapter = moshi.adapter(Intervals.class); + Intervals intervals = new Intervals(); + try { + intervals = jsonAdapter.fromJson(string); + } catch (IOException e) { + } + + + return intervals; + } + + @TypeConverter + public String iperf3IntervalsToString(Intervals intervals) { + + Moshi moshi = new Moshi.Builder() + .add(PolymorphicJsonAdapterFactory.of(Stream.class, "streamType") + .withSubtype(TCP_DL_STREAM.class, STREAM_TYPE.TCP_DL.toString()) + .withSubtype(TCP_UL_STREAM.class, STREAM_TYPE.TCP_UL.toString()) + .withSubtype(UDP_DL_STREAM.class, STREAM_TYPE.UDP_DL.toString()) + .withSubtype(UDP_UL_STREAM.class, STREAM_TYPE.UDP_UL.toString())) + .add(PolymorphicJsonAdapterFactory.of(Sum.class, "sumType") + .withSubtype(TCP_DL_SUM.class, SUM_TYPE.TCP_DL.toString()) + .withSubtype(TCP_UL_SUM.class, SUM_TYPE.TCP_UL.toString()) + .withSubtype(UDP_DL_SUM.class, SUM_TYPE.UDP_DL.toString()) + .withSubtype(UDP_UL_SUM.class, SUM_TYPE.UDP_UL.toString())) + .add(new ArrayListIntervalsAdapter()) + .add(new ArrayListStreamsAdapter()) + .build(); + JsonAdapter jsonAdapter = moshi.adapter(Intervals.class); + + return jsonAdapter.toJson(intervals); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3StartConverter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3StartConverter.java new file mode 100644 index 00000000..888381bd --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/Iperf3StartConverter.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter; + +import android.util.Log; + +import androidx.room.ProvidedTypeConverter; +import androidx.room.TypeConverter; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.start.Start; + + +@ProvidedTypeConverter +public class Iperf3StartConverter { + private final String TAG = "Iperf3StartConverter"; + @TypeConverter + public Start StringToIperf3Start(String string) { + Start start = new Start(); + try { + start = new Gson().fromJson(string, Start.class); + } catch (JsonSyntaxException e){ + Log.d(TAG, "StringToIperf3Start: "+e); + } + return start; + } + + @TypeConverter + public String Iperf3StartToString(Start example) { + return new Gson().toJson(example); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/MetricConverter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/MetricConverter.java new file mode 100644 index 00000000..d122c752 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/Converter/MetricConverter.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter; + +import androidx.room.ProvidedTypeConverter; +import androidx.room.TypeConverter; + +import com.google.gson.Gson; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricCalculator; + + +@ProvidedTypeConverter +public class MetricConverter { + private double median; + private double mean; + private double max; + private double min; + private double last; + @TypeConverter + public MetricCalculator fromJSONString(String string) { + return new Gson().fromJson(string, MetricCalculator.class); + } + + @TypeConverter + public String toJSONString(MetricCalculator example) { + return new Gson().toJson(example); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListIntervalsAdapter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListIntervalsAdapter.java new file mode 100644 index 00000000..38925a5a --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListIntervalsAdapter.java @@ -0,0 +1,24 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.MoshiAdapter; + +import com.squareup.moshi.FromJson; +import com.squareup.moshi.ToJson; + +import java.util.ArrayList; +import java.util.List; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Intervals; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; + +public class ArrayListIntervalsAdapter { + @ToJson + List ToJson(Intervals interval) { + return interval.getIntervalArrayList(); + } + + @FromJson + Intervals FromJson(List listInterval) { + Intervals intervals = new Intervals(); + intervals.setIntervals(new ArrayList<>(listInterval)); + return intervals; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListStreamsAdapter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListStreamsAdapter.java new file mode 100644 index 00000000..a238e9c2 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/MoshiAdapter/ArrayListStreamsAdapter.java @@ -0,0 +1,29 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.MoshiAdapter; + +import com.squareup.moshi.FromJson; +import com.squareup.moshi.ToJson; + +import java.util.ArrayList; +import java.util.List; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Intervals; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.Stream; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.Streams; + +public class ArrayListStreamsAdapter { + @ToJson + List ToJson(Streams streams) { + return streams.getStreamArrayList(); + } + + @FromJson + Streams FromJson(List listStreams) { + + + + Streams streams = new Streams(); + streams.setStreams(new ArrayList<>(listStreams)); + return streams; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ResultsDataBase.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3ResultsDataBase.java similarity index 54% rename from app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ResultsDataBase.java rename to app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3ResultsDataBase.java index d9b01a29..981c2131 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ResultsDataBase.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3ResultsDataBase.java @@ -6,7 +6,7 @@ * SPDX-License-Identifier: BSD-3-Clause-Clear */ -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult; import android.content.Context; @@ -14,14 +14,22 @@ import androidx.room.Room; import androidx.room.RoomDatabase; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3ErrorConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3InputConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3IntervalsConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3StartConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.MetricConverter; + @Database( entities = {Iperf3RunResult.class}, version = 3 ) + public abstract class Iperf3ResultsDataBase extends RoomDatabase { + private static volatile Iperf3ResultsDataBase INSTANCE; - static Iperf3ResultsDataBase getDatabase(final Context context) { + public static Iperf3ResultsDataBase getDatabase(final Context context) { if (INSTANCE == null) { synchronized (Iperf3ResultsDataBase.class) { if (INSTANCE == null) { @@ -29,7 +37,12 @@ static Iperf3ResultsDataBase getDatabase(final Context context) { INSTANCE = Room.databaseBuilder(context.getApplicationContext(), Iperf3ResultsDataBase.class, "iperf3_result_database") .addTypeConverter(new Iperf3InputConverter()) + .addTypeConverter(new Iperf3IntervalsConverter()) + .addTypeConverter(new Iperf3StartConverter()) + .addTypeConverter(new MetricConverter()) + .addTypeConverter(new Iperf3ErrorConverter()) .allowMainThreadQueries() + .enableMultiInstanceInvalidation() .build(); } } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResult.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResult.java new file mode 100644 index 00000000..9a5dfe2c --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResult.java @@ -0,0 +1,70 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Embedded; +import androidx.room.Entity; +import androidx.room.PrimaryKey; +import androidx.room.TypeConverters; + +import java.sql.Timestamp; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3ErrorConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3InputConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3IntervalsConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3StartConverter; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.MetricConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Intervals; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Error; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.start.Start; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricCalculator; + +@Entity(tableName = "iperf3_result_database") +@TypeConverters({Iperf3InputConverter.class, Iperf3IntervalsConverter.class, Iperf3StartConverter.class, MetricConverter.class}) +public class Iperf3RunResult { + @NonNull + @PrimaryKey + public String uid; + + @ColumnInfo(name = "result") + public int result; + + @ColumnInfo(name = "uploaded") + public boolean uploaded; + + @ColumnInfo(name = "timestamp") + public long timestamp; + + @ColumnInfo(name = "input") + public Iperf3Input input; + + @ColumnInfo(name = "_start") + public Start start; + + @ColumnInfo(name = "_end") + public String end; + @ColumnInfo(name = "_intervals") + public Intervals intervals; + @ColumnInfo(name = "metricUL") + public MetricCalculator metricUL; + @ColumnInfo(name = "metricDL") + public MetricCalculator metricDL; + + @ColumnInfo(name = "error") + @TypeConverters({Iperf3ErrorConverter.class}) + public Error error; + + public Iperf3RunResult(String uid, int result, boolean upload, Iperf3Input input, + Timestamp timestamp) { + this.uid = uid; + this.result = result; + this.uploaded = upload; + this.input = input; + this.timestamp = timestamp.getTime(); + } + + public Iperf3RunResult() { + } +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResultDao.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResultDao.java new file mode 100644 index 00000000..ed26a97c --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Database/RunResult/Iperf3RunResultDao.java @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult; + +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; +import androidx.room.TypeConverters; + +import java.util.List; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3ErrorConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.Iperf3IntervalsConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.Converter.MetricConverter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Intervals; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Error; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricCalculator; + +@Dao +public interface Iperf3RunResultDao { + @Query("SELECT * FROM iperf3_result_database") + LiveData> getAll(); + + @Query("SELECT uid FROM iperf3_result_database ORDER BY timestamp DESC") + List getIDs(); + + @Query("SELECT * FROM iperf3_result_database WHERE uid = :comp_uid") + Iperf3RunResult getRunResult(String comp_uid); + + @Query("SELECT timestamp FROM iperf3_result_database WHERE uid = :comp_uid") + long getTimestampFromUid(String comp_uid); + + @Query("SELECT _intervals FROM iperf3_result_database WHERE uid = :comp_uid") + @TypeConverters({Iperf3IntervalsConverter.class}) + Intervals getIntervals(String comp_uid); + + @Query("UPDATE iperf3_result_database SET _intervals = :intervals WHERE uid = :uid") + @TypeConverters({Iperf3IntervalsConverter.class}) + void updateIntervals(String uid, Intervals intervals); + @Query("UPDATE iperf3_result_database SET metricUL = :metricUL WHERE uid = :uid") + @TypeConverters({MetricConverter.class}) + void updateMetricUL(String uid, MetricCalculator metricUL); + + @Query("UPDATE iperf3_result_database SET metricDL = :metricDL WHERE uid = :uid") + @TypeConverters({MetricConverter.class}) + void updateMetricDL(String uid, MetricCalculator metricDL); + + @Query("UPDATE iperf3_result_database SET _start = :start WHERE uid = :uid") + void updateStart(String uid, String start); + + @Query("UPDATE iperf3_result_database SET _end = :end WHERE uid = :uid") + void updateEnd(String uid, String end); + + @Query("UPDATE iperf3_result_database SET uploaded = :uploaded WHERE uid = :uid") + void updateUploaded(String uid, boolean uploaded); + + @Query("UPDATE iperf3_result_database SET result = :result WHERE uid = :uid") + void updateResult(String uid, int result); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(Iperf3RunResult iperf3RunResult); + + @Query("SELECT metricDL FROM iperf3_result_database WHERE uid = :uid") + @TypeConverters({MetricConverter.class}) + MetricCalculator getMetricDL(String uid); + + @Query("SELECT metricUL FROM iperf3_result_database WHERE uid = :uid") + @TypeConverters({MetricConverter.class}) + MetricCalculator getMetricUL(String uid); + + @Query("SELECT error FROM iperf3_result_database WHERE uid = :uid") + @TypeConverters({Iperf3ErrorConverter.class}) + Error getError(String uid); + + @Query("UPDATE iperf3_result_database SET error = :error WHERE uid = :uid") + @TypeConverters({Iperf3ErrorConverter.class}) + void updateError(String uid, Error error); + + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Fragments/Iperf3Fragment.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Fragments/Iperf3Fragment.java new file mode 100644 index 00000000..0ef11e4d --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Fragments/Iperf3Fragment.java @@ -0,0 +1,513 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Fragments; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import android.content.Context; +import android.graphics.Color; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.work.ListenableWorker; +import androidx.work.WorkInfo; +import androidx.work.WorkQuery; +import androidx.work.multiprocess.RemoteWorkManager; + +import com.google.android.material.bottomsheet.BottomSheetBehavior; +import com.google.android.material.button.MaterialButton; +import com.google.android.material.button.MaterialButtonToggleGroup; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.progressindicator.LinearProgressIndicator; +import com.google.android.material.textfield.TextInputEditText; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + + +import org.json.JSONException; + +import java.io.File; +import java.io.IOException; +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3ResultsDataBase; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResult; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResultDao; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Iperf3Executor; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Iperf3RecyclerViewAdapter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.Sum; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3ExecutorWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3MonitorWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.METRIC_TYPE; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricCalculator; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricView; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.Iperf3Parameter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; + +public class Iperf3Fragment extends Fragment { + + private static final String ARG_POSITION = "position"; + private Iperf3Input iperf3Input; + private Context ct; + private MaterialButton sendBtn; + private View view; + private TextInputEditText ip; + private TextInputEditText port; + private TextInputEditText bitrate; + private TextInputEditText duration; + private TextInputEditText interval; + private TextInputEditText bytes; + private TextInputEditText streams; + private TextInputEditText cport; + + + private MaterialButtonToggleGroup mode; + private MaterialButtonToggleGroup protocol; + private MaterialButtonToggleGroup direction; + + private MaterialButton modeClient; + private MaterialButton modeServer; + private MaterialButton protocolTCP; + private MaterialButton protocolUDP; + private MaterialButton directionUp; + private MaterialButton directionDown; + private MaterialButton directonBidir; + private Handler handler; + private RecyclerView recyclerView; + private SharedPreferencesGrouper spg; + + private String TAG = "Iperf3CardFragment"; + private String uuid; + private Iperf3RecyclerViewAdapter adapter; + private Iperf3RunResultDao iperf3RunResultDao; + private Iperf3ResultsDataBase iperf3ResultsDataBase; + private BottomSheetBehavior bottomSheetBehavior; + private FloatingActionButton fab; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.ct = requireContext(); + } + + /** + * Create a text watcher + * @param consumer + * @param name + * @return + */ + private TextWatcher createTextWatcher(Consumer consumer, String name) { + return new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + consumer.accept(charSequence.toString()); + spg.getSharedPreference(SPType.iperf3_sp).edit().putString(name, charSequence.toString()).apply(); + } + + @Override + public void afterTextChanged(Editable editable) { + } + }; + } + + /** + * Set up the text watchers + */ + private void setupTextWatchers() { + ip.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setHost(s), Iperf3Parameter.HOST)); + port.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setPort(Integer.parseInt("0"+s)), Iperf3Parameter.PORT)); + bitrate.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setBandwidth(s), Iperf3Parameter.BITRATE)); + duration.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setTime(Integer.parseInt("0"+s)), Iperf3Parameter.TIME)); + interval.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setInterval(Double.parseDouble("0"+s)), Iperf3Parameter.INTERVAL)); + bytes.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setBytes(s), Iperf3Parameter.BYTES)); + streams.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setParallel(Integer.parseInt("0"+s)), Iperf3Parameter.PARALLEL)); + cport.addTextChangedListener(createTextWatcher(s -> iperf3Input.getParameter().setCport(Integer.parseInt("0"+s) ), Iperf3Parameter.CPORT)); + } + + /** + * Set the text from the shared preferences + * @param editText + * @param key + */ + private void setTextFromSharedPreferences(TextInputEditText editText, String key) { + if (spg.getSharedPreference(SPType.iperf3_sp).contains(key)) { + editText.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(key, "")); + } + } + + Runnable runnable = new Runnable() { + @Override + public void run() { + RemoteWorkManager remoteWorkManager = RemoteWorkManager.getInstance(ct); + + WorkQuery workQuery = WorkQuery.Builder + .fromTags(Arrays.asList(iperf3Input.getTestUUID())) + .build(); + ListenableFuture> foobar = remoteWorkManager.getWorkInfos(workQuery); + Futures.addCallback( + foobar, + new FutureCallback<>() { + public void onSuccess(List result) { + + for (WorkInfo workInfo : result) { + Log.d(TAG, "onSuccess: workInfoTags: "+ workInfo.getTags()); + Log.d(TAG, "onSuccess workInfo State: " + workInfo.getState()); + Log.d(TAG, "onSuccess workInfo isFinished: " + workInfo.getState().isFinished()); + + if (workInfo.getTags().contains(Iperf3MonitorWorker.class.getCanonicalName())) { + Log.d(TAG, "onSuccess: "+Iperf3MonitorWorker.class.getName()+" in state"+workInfo.getState()); + switch (workInfo.getState()) { + case SUCCEEDED: + adapter.notifyDataSetChanged(); + break; + case CANCELLED: + case FAILED: + try { + Log.d(TAG, "onSuccess: going sleeping"); + Thread.sleep(1000); //todo handle better, is needed because write to db is to slow + Log.d(TAG, "onSuccess: "+iperf3RunResultDao.getRunResult(uuid).error); + Log.d(TAG, "onSuccess: woke up"); + } catch (InterruptedException e) { + + } + adapter.notifyDataSetChanged(); + break; + case BLOCKED: + case ENQUEUED: + case RUNNING: + adapter.notifyDataSetChanged(); + handler.postDelayed(runnable, 500); + break; + } + } else if(workInfo.getTags().contains(Iperf3ExecutorWorker.class.getCanonicalName())){ + Log.d(TAG, "onSuccess: "+Iperf3ExecutorWorker.class.getName()+" in state"+workInfo.getState()); + switch (workInfo.getState()) { + case SUCCEEDED: + adapter.notifyDataSetChanged(); + break; + case CANCELLED: + case FAILED: + iperf3RunResultDao.updateResult(uuid, -1); + remoteWorkManager.cancelAllWorkByTag(iperf3Input.getTestUUID()); + adapter.notifyDataSetChanged(); + break; + case BLOCKED: + case ENQUEUED: + case RUNNING: + String line = workInfo.getProgress().getString("interval"); + Log.d(TAG, "onSuccess: "+line); + adapter.notifyDataSetChanged(); + + break; + } + } + } + adapter.notifyDataSetChanged(); + } + + public void onFailure(@NonNull Throwable thrown) { + // handle failure + } + }, + getContext().getMainExecutor() + ); + + } + }; + + /** + * Set the texts from the shared preferences + */ + private void setTextsFromSharedPreferences(){ + setTextFromSharedPreferences(ip, Iperf3Parameter.HOST); + setTextFromSharedPreferences(port, Iperf3Parameter.PORT); + setTextFromSharedPreferences(bitrate, Iperf3Parameter.BITRATE); + setTextFromSharedPreferences(duration, Iperf3Parameter.TIME); + setTextFromSharedPreferences(interval, Iperf3Parameter.INTERVAL); + setTextFromSharedPreferences(bytes, Iperf3Parameter.BYTES); + setTextFromSharedPreferences(streams, Iperf3Parameter.STREAMS); + setTextFromSharedPreferences(cport, Iperf3Parameter.CPORT); + } + + private void setupBottomSheet(){ + bottomSheetBehavior = BottomSheetBehavior.from(view.findViewById(R.id.standard_bottom_sheet)); + bottomSheetBehavior.setPeekHeight(100); + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + bottomSheetBehavior.setHideable(false); + + } + private void setupDatabase(){ + iperf3ResultsDataBase = Iperf3ResultsDataBase.getDatabase(ct); + iperf3RunResultDao = iperf3ResultsDataBase.iperf3RunResultDao(); + } + private void setupRecyclerView(){ + LinearLayoutManager linearLayoutManager = + new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + + recyclerView = view.findViewById(R.id.runners_list); + adapter = new Iperf3RecyclerViewAdapter(fab); + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + Log.d(TAG, "onChanged: "+adapter.getSelectedUUID()); + } + }); + + recyclerView.setAdapter(adapter); + recyclerView.setLayoutManager(linearLayoutManager); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + view = inflater.inflate(R.layout.fragment_iperf3_input, container, false); + // Initialize the TextView + String iperf3UUID = UUID.randomUUID().toString(); + Iperf3Parameter iperf3Parameter = new Iperf3Parameter(iperf3UUID); + iperf3Input = new Iperf3Input(iperf3Parameter, ""); + sendBtn = view.findViewById(R.id.iperf3_send); + spg = SharedPreferencesGrouper.getInstance(ct); + handler = new Handler(Looper.getMainLooper()); + + setupBottomSheet(); + sendBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + uuid = UUID.randomUUID().toString(); + iperf3Input.setTestUUID(uuid); + iperf3Input.getParameter().setTestUUID(uuid); + iperf3Input.getParameter().updatePaths(); + iperf3Input.setTimestamp(new Timestamp(System.currentTimeMillis())); + + File logFile = new File(iperf3Input.getParameter().getLogfile()); + File rawPath = new File(Iperf3Parameter.rawDirPath); + + if(!rawPath.exists()) { + rawPath.mkdirs(); + } + try { + logFile.createNewFile(); + Log.d(TAG, "onClick: created File: "+logFile.toString()); + } catch (Exception e) { + Log.d(TAG, "startRemoteWork: "+e); + } + + + + Iperf3Executor iperf3Executor = new Iperf3Executor(iperf3Input, getContext()); + iperf3Executor.execute(); + Log.d(TAG, "onClick: "+iperf3Input.getParameter().getTime()); + + + Iperf3RunResult iperf3RunResult = new Iperf3RunResult(iperf3Input.getTestUUID(), -100, false, iperf3Input, new java.sql.Timestamp(System.currentTimeMillis())); + iperf3RunResultDao.insert(iperf3RunResult); + + handler.post(runnable); // start the first execution + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + adapter.notifyDataSetChanged(); + + } + }); + //fab = view.findViewById(R.id.iperf3_influx_upload_button); + ip = view.findViewById(R.id.iperf3_ip); + port = view.findViewById(R.id.iperf3_port); + bitrate = view.findViewById(R.id.iperf3_bandwidth); + duration = view.findViewById(R.id.iperf3_duration); + interval = view.findViewById(R.id.iperf3_interval); + bytes = view.findViewById(R.id.iperf3_bytes); + streams = view.findViewById(R.id.iperf3_streams); + cport = view.findViewById(R.id.iperf3_cport); + + + mode = view.findViewById(R.id.iperf3_mode_toggle_group); + protocol = view.findViewById(R.id.iperf3_protocol_toggle_group); + direction = view.findViewById(R.id.iperf3_direction_toggle_group); + + modeClient = view.findViewById(R.id.iperf3_client_button); + modeServer = view.findViewById(R.id.iperf3_server_button); + + protocolTCP = view.findViewById(R.id.iperf3_tcp_button); + protocolUDP = view.findViewById(R.id.iperf3_udp_button); + + directionDown = view.findViewById(R.id.iperf3_download_button); + directionUp = view.findViewById(R.id.iperf3_upload_button); + directonBidir = view.findViewById(R.id.iperf3_bidir_button); + + setupTextWatchers(); + setTextsFromSharedPreferences(); + try { + switch (Iperf3Parameter.Iperf3Mode.valueOf(spg.getSharedPreference(SPType.iperf3_sp).getString(Iperf3Parameter.MODE, String.valueOf(Iperf3Parameter.Iperf3Mode.UNDEFINED)))){ + case CLIENT: + updateModeState(modeClient, modeServer, Iperf3Parameter.Iperf3Mode.CLIENT); + break; + case SERVER: + updateModeState(modeServer, modeClient, Iperf3Parameter.Iperf3Mode.SERVER); + break; + case UNDEFINED: + default: + modeClient.setBackgroundColor(Color.TRANSPARENT); + modeServer.setBackgroundColor(Color.TRANSPARENT); + spg.getSharedPreference(SPType.iperf3_sp).edit().putString(Iperf3Parameter.MODE, Iperf3Parameter.Iperf3Mode.UNDEFINED.toString()).apply(); + break; + } + } catch (IllegalArgumentException e) { + Log.e(TAG, "onCreateView: ", e); + } + try { + switch (Iperf3Parameter.Iperf3Protocol.valueOf(spg.getSharedPreference(SPType.iperf3_sp).getString(Iperf3Parameter.PROTOCOL, Iperf3Parameter.Iperf3Protocol.UNDEFINED.toString()))){ + case TCP: + updateProtocolState(protocolTCP, protocolUDP, Iperf3Parameter.Iperf3Protocol.TCP); + break; + case UDP: + updateProtocolState(protocolUDP, protocolTCP, Iperf3Parameter.Iperf3Protocol.UDP); + break; + case UNDEFINED: + default: + protocolTCP.setBackgroundColor(Color.TRANSPARENT); + protocolUDP.setBackgroundColor(Color.TRANSPARENT); + spg.getSharedPreference(SPType.iperf3_sp).edit().putString(Iperf3Parameter.PROTOCOL, Iperf3Parameter.Iperf3Protocol.UNDEFINED.toString()).apply(); + break; + } + } catch (IllegalArgumentException e) { + Log.e(TAG, "onCreateView: ", e); + } + try { + switch (Iperf3Parameter.Iperf3Direction.valueOf(spg.getSharedPreference(SPType.iperf3_sp).getString(Iperf3Parameter.DIRECTION, Iperf3Parameter.Iperf3Direction.UNDEFINED.toString()))) { + case UP: + updateDirectionState(directionUp, directionDown, directonBidir, Iperf3Parameter.Iperf3Direction.UP); + break; + case DOWN: + updateDirectionState(directionDown, directionUp, directonBidir, Iperf3Parameter.Iperf3Direction.DOWN); + break; + case BIDIR: + updateDirectionState(directonBidir, directionUp, directionDown, Iperf3Parameter.Iperf3Direction.BIDIR); + break; + case UNDEFINED: + default: + directionUp.setBackgroundColor(Color.TRANSPARENT); + directionDown.setBackgroundColor(Color.TRANSPARENT); + directonBidir.setBackgroundColor(Color.TRANSPARENT); + break; + } + } catch (IllegalArgumentException e) { + Log.e(TAG, "onCreateView: ", e); + } + + mode.addOnButtonCheckedListener(new MaterialButtonToggleGroup.OnButtonCheckedListener() { + @Override + public void onButtonChecked(MaterialButtonToggleGroup group, int checkedId, boolean isChecked) { + if (isChecked) { + switch (checkedId) { + case R.id.iperf3_client_button: + updateModeState(modeClient, modeServer, Iperf3Parameter.Iperf3Mode.CLIENT); + break; + case R.id.iperf3_server_button: + updateModeState(modeServer, modeClient, Iperf3Parameter.Iperf3Mode.SERVER); + break; + } + } + } + }); + protocol.addOnButtonCheckedListener(new MaterialButtonToggleGroup.OnButtonCheckedListener() { + @Override + public void onButtonChecked(MaterialButtonToggleGroup group, int checkedId, boolean isChecked) { + if (isChecked) { + switch (checkedId) { + case R.id.iperf3_tcp_button: + updateProtocolState(protocolTCP, protocolUDP, Iperf3Parameter.Iperf3Protocol.TCP); + break; + case R.id.iperf3_udp_button: + updateProtocolState(protocolUDP, protocolTCP, Iperf3Parameter.Iperf3Protocol.UDP); + break; + } + } + } + }); + direction.addOnButtonCheckedListener(new MaterialButtonToggleGroup.OnButtonCheckedListener() { + @Override + public void onButtonChecked(MaterialButtonToggleGroup group, int checkedId, boolean isChecked) { + if (isChecked) { + switch (checkedId) { + case R.id.iperf3_upload_button: + updateDirectionState(directionUp, directionDown, directonBidir, Iperf3Parameter.Iperf3Direction.UP); + break; + case R.id.iperf3_download_button: + updateDirectionState(directionDown, directionUp, directonBidir, Iperf3Parameter.Iperf3Direction.DOWN); + break; + case R.id.iperf3_bidir_button: + updateDirectionState(directonBidir, directionUp, directionDown, Iperf3Parameter.Iperf3Direction.BIDIR); + break; + } + } + } + }); + setupDatabase(); + setupBottomSheet(); + setupRecyclerView(); + return view; + } + + @Override + public void onResume() { + super.onResume(); + view.requestLayout(); + } + @Override + public void onDestroy() { + super.onDestroy(); + handler.removeCallbacks(runnable); + } + + + private void updateModeState(MaterialButton activeButton, MaterialButton inactiveButton, Iperf3Parameter.Iperf3Mode protocol) { + activeButton.setBackgroundColor(getResources().getColor(R.color.purple_500, null)); + inactiveButton.setBackgroundColor(Color.TRANSPARENT); + iperf3Input.getParameter().setMode(protocol); + spg.getSharedPreference(SPType.iperf3_sp).edit().putString(Iperf3Parameter.MODE, protocol.toString()).apply(); + } + + private void updateProtocolState(MaterialButton activeButton, MaterialButton inactiveButton, Iperf3Parameter.Iperf3Protocol protocol) { + activeButton.setBackgroundColor(getResources().getColor(R.color.purple_500, null)); + inactiveButton.setBackgroundColor(Color.TRANSPARENT); + iperf3Input.getParameter().setProtocol(protocol); + spg.getSharedPreference(SPType.iperf3_sp).edit().putString(Iperf3Parameter.PROTOCOL, protocol.toString()).apply(); + } + + private void updateDirectionState(MaterialButton activeButton, MaterialButton inactiveButton1, MaterialButton inactiveButton2, Iperf3Parameter.Iperf3Direction direction) { + activeButton.setBackgroundColor(getResources().getColor(R.color.purple_500, null)); + inactiveButton1.setBackgroundColor(Color.TRANSPARENT); + inactiveButton2.setBackgroundColor(Color.TRANSPARENT); + iperf3Input.getParameter().setDirection(direction); + spg.getSharedPreference(SPType.iperf3_sp).edit().putString(Iperf3Parameter.DIRECTION, direction.toString()).apply(); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Intervals.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Intervals.java index 08635896..37c05662 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Intervals.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Intervals.java @@ -16,4 +16,8 @@ public ArrayList getIntervalArrayList(){ return intervals; } + public void setIntervals(ArrayList intervals) { + this.intervals.clear(); + this.intervals.addAll(intervals); + } } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Executor.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Executor.java new file mode 100644 index 00000000..c94da161 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Executor.java @@ -0,0 +1,78 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; + +import android.annotation.SuppressLint; +import android.content.Context; + +import androidx.work.Constraints; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkContinuation; +import androidx.work.WorkManager; +import androidx.work.multiprocess.RemoteWorkContinuation; +import androidx.work.multiprocess.RemoteWorkManager; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.Worker.InfluxDB2xUploadWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor.Iperf3ServiceWorkerOne; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor.Iperf3MonitorServiceWorkerOne; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3ExecutorWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3MonitorWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker.Iperf3ToLineProtocolWorker; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; + +public class Iperf3Executor { + private static final String TAG = "Iperf3Executor"; + private RemoteWorkManager remoteWorkManager; + private WorkManager workManager; + private Context context; + private SharedPreferencesGrouper spg; + private RemoteWorkContinuation remoteWorkContinuation; + private WorkContinuation localWorkContinuation; + @SuppressLint("EnqueueWork") + public Iperf3Executor(Iperf3Input iperf3Input, Context context){ + + if(iperf3Input == null){ + throw new IllegalArgumentException("Iperf3Input cannot be null"); + } + if(context == null){ + throw new IllegalArgumentException("Context cannot be null"); + } + this.context = context; + this.spg = SharedPreferencesGrouper.getInstance(this.context); + this.remoteWorkManager = RemoteWorkManager.getInstance(this.context); + this.workManager = WorkManager.getInstance(this.context); + OneTimeWorkRequest iperf3ExecutorWorker = new OneTimeWorkRequest.Builder(Iperf3ExecutorWorker.class) + .setInputData(iperf3Input.getInputAsDataBuilder(0, context.getPackageName(), Iperf3ServiceWorkerOne.class.getName()).build()) + .addTag(iperf3Input.getTestUUID()) + .build(); + OneTimeWorkRequest iperf3MonitorWorker = new OneTimeWorkRequest.Builder(Iperf3MonitorWorker.class) + .setInputData(iperf3Input.getInputAsDataBuilder(0, context.getPackageName(), Iperf3MonitorServiceWorkerOne.class.getName()).build()) + .addTag(iperf3Input.getTestUUID()) + .build(); + OneTimeWorkRequest iPerf3ToLineProtocolWorker = new OneTimeWorkRequest.Builder(Iperf3ToLineProtocolWorker.class) + .setInputData(iperf3Input.getInputAsDataBuilder(0).build()) + .addTag(iperf3Input.getTestUUID()) + .build(); + OneTimeWorkRequest influxDB2xUploadWorker = new OneTimeWorkRequest.Builder(InfluxDB2xUploadWorker.class) + .setInputData(iperf3Input.getInputAsDataBuilder(0).build()) + .addTag(iperf3Input.getTestUUID()) + .build(); + + + this.remoteWorkContinuation = this.remoteWorkManager.beginWith(Arrays.asList(iperf3ExecutorWorker, iperf3MonitorWorker)).then(iPerf3ToLineProtocolWorker); + if(spg.getSharedPreference(SPType.logging_sp).getBoolean("enable_influx", false)){ + this.remoteWorkContinuation = remoteWorkContinuation.then(influxDB2xUploadWorker); + } + } + + public void execute(){ + this.remoteWorkContinuation.enqueue(); + + } + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Fragment.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Fragment.java deleted file mode 100644 index ff16e37c..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Fragment.java +++ /dev/null @@ -1,857 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Peter Hasse - * SPDX-FileCopyrightText: 2023 Johann Hackler - * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS - * - * SPDX-License-Identifier: BSD-3-Clause-Clear - */ - -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.Looper; -import android.system.ErrnoException; -import android.system.Os; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentResultListener; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.Observer; -import androidx.navigation.NavController; -import androidx.navigation.fragment.NavHostFragment; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkInfo; -import androidx.work.WorkManager; - -import com.google.android.material.progressindicator.LinearProgressIndicator; -import com.google.common.util.concurrent.ListenableFuture; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; - -public class Iperf3Fragment extends Fragment { - private static final String TAG = "iperf3InputFragment"; - private final int SHOW_PROGRESSBAR = 3000; - private final String IPERF3IP = "iperf3IP"; - private final String IPERF3PORT = "iperf3Port"; - private final String IPERF3BANDWIDTH = "iperf3Bandwidth"; - private final String IPERF3DURATION = "iperf3Duration"; - private final String IPERF3INTERVAL = "iperf3Interval"; - private final String IPERF3BYTES = "iperf3Bytes"; - private final String IPERF3STREAMS = "iperf3Streams"; - private final String IPERF3BIDIR = "iperf3BiDir"; - private final String IPERF3REVERSE = "iperf3Reverse"; - private final String IPERF3JSON = "iperf3Json"; - private final String IPERF3ONEOFF = "iperf3OneOff"; - private final String IPERF3IDXPROTOCOL = "iperf3IdxProtocol"; - private final String IPERF3IDXMODE = "iperf3IdxMode"; - private final String IPERF3CPORT = "iperf3cport"; - private CheckBox iperf3BiDir; - private CheckBox iperf3Reverse; - - private CheckBox iperf3OneOff; - private EditText iperf3EtIp; - private EditText iperf3EtPort; - private EditText iperf3EtBandwidth; - private EditText iperf3EtDuration; - private EditText iperf3EtInterval; - private EditText iperf3EtBytes; - private EditText iperf3EtStreams; - private EditText iperf3Cport; - private Button sendBtn; - private Button instancesBtn; - private Spinner protocolSpinner; - private Spinner iperf3ModeSpinner; - private Iperf3RunResultDao iperf3RunResultDao; - private LinearProgressIndicator progressIndicator; - private int[] failedColors; - private int[] runningColors; - private int[] succesColors; - private LinkedList editTexts; - private String rawIperf3file; - private String logFileDir; - private String logFileName; - private View v; - private SharedPreferencesGrouper spg; - private Iperf3Input input; - private WorkManager iperf3WM; - private Iperf3ResultsDataBase db; - private ArrayList uids; - private Context ct; - private final Runnable progressbarUpdate = new Runnable() { - @Override - public void run() { - progressIndicator.setVisibility(LinearProgressIndicator.INVISIBLE); - progressIndicator.setIndicatorColor(runningColors); - } - }; - - @Override - public void onPause() { - super.onPause(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.input = new Iperf3Input(); - this.db = Iperf3ResultsDataBase.getDatabase(getActivity().getApplicationContext()); - this.uids = new ArrayList<>(this.db.iperf3RunResultDao().getIDs()); - this.iperf3WM = WorkManager.getInstance(getActivity().getApplicationContext()); - this.logFileDir = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) - .getAbsolutePath() + "/omnt/iperf3RawLogs/"; - this.iperf3RunResultDao = db.iperf3RunResultDao(); - File iperf3Path = new File(this.logFileDir); - if (!iperf3Path.exists()) { - iperf3Path.mkdir(); - } - this.ct = requireContext(); - this.spg = SharedPreferencesGrouper.getInstance(this.ct); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - - NavController navController = NavHostFragment.findNavController(this); - MutableLiveData liveData = navController.getCurrentBackStackEntry() - .getSavedStateHandle() - .getLiveData("uid"); - - - - liveData.observe(getViewLifecycleOwner(), new Observer() { - @Override - public void onChanged(String s) { - Iperf3RunResult iperf3RunResult = db.iperf3RunResultDao().getRunResult(s); - - iperf3EtIp.setText(iperf3RunResult.input.iperf3IP); - iperf3EtPort.setText(iperf3RunResult.input.iperf3Port); - iperf3EtBandwidth.setText(iperf3RunResult.input.iperf3Bandwidth); - iperf3EtDuration.setText(iperf3RunResult.input.iperf3Duration); - iperf3EtInterval.setText(iperf3RunResult.input.iperf3Interval); - iperf3EtBytes.setText(iperf3RunResult.input.iperf3Bytes); - iperf3Cport.setText(iperf3RunResult.input.iperf3Cport); - - iperf3Reverse.setChecked(iperf3RunResult.input.iperf3Reverse); - iperf3BiDir.setChecked(iperf3RunResult.input.iperf3BiDir); - iperf3OneOff.setChecked(iperf3RunResult.input.iperf3OneOff); - protocolSpinner.setSelection(iperf3RunResult.input.iperf3IdxProtocol); - iperf3ModeSpinner.setSelection(iperf3RunResult.input.iperf3IdxMode); - - - } - }); - } - - - private void saveTextInputToSharedPreferences(EditText field, String name) { - field.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { - - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - spg.getSharedPreference(SPType.iperf3_sp).edit().putString(name, field.getText().toString()).apply(); - } - - @Override - public void afterTextChanged(Editable editable) { - - } - }); - - } - - - - private void saveCheckboxInputToSharedPreferences(CheckBox box, String name) { - box.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - spg.getSharedPreference(SPType.iperf3_sp).edit().putBoolean(name, box.isChecked()).apply(); - } - }); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { - v = inflater.inflate(R.layout.fragment_iperf3_input, parent, false); - iperf3EtIp = v.findViewById(R.id.iperf3_ip); - iperf3EtPort = v.findViewById(R.id.iperf3_port); - iperf3EtBandwidth = v.findViewById(R.id.iperf3_bandwidth); - iperf3EtDuration = v.findViewById(R.id.iperf3_duration); - - - - - iperf3EtInterval = v.findViewById(R.id.iperf3_interval); - iperf3EtBytes = v.findViewById(R.id.iperf3_bytes); - iperf3EtStreams = v.findViewById(R.id.iperf3_streams); - iperf3Cport = v.findViewById(R.id.iperf3_cport); - progressIndicator = v.findViewById(R.id.iperf3_progress); - - iperf3EtDuration.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - iperf3EtBytes.setEnabled(s.length() <= 0); - } - - @Override - public void afterTextChanged(Editable s) { - - } - }); - - - iperf3EtBytes.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - iperf3EtDuration.setEnabled(s.length() <= 0); - } - - @Override - public void afterTextChanged(Editable s) { - - } - }); - - saveTextInputToSharedPreferences(iperf3EtIp, IPERF3IP); - saveTextInputToSharedPreferences(iperf3EtPort, IPERF3PORT); - saveTextInputToSharedPreferences(iperf3EtBandwidth, IPERF3BANDWIDTH); - saveTextInputToSharedPreferences(iperf3EtDuration, IPERF3DURATION); - saveTextInputToSharedPreferences(iperf3EtInterval, IPERF3INTERVAL); - saveTextInputToSharedPreferences(iperf3EtBytes, IPERF3BYTES); - saveTextInputToSharedPreferences(iperf3EtStreams, IPERF3STREAMS); - saveTextInputToSharedPreferences(iperf3Cport, IPERF3CPORT); - - failedColors = new int[] {getContext().getColor(R.color.crimson), - getContext().getColor(R.color.crimson), getContext().getColor(R.color.crimson)}; - runningColors = new int[] {getContext().getColor(R.color.purple_500), - getContext().getColor(R.color.crimson), getContext().getColor(R.color.forestgreen)}; - succesColors = new int[] {getContext().getColor(R.color.forestgreen), - getContext().getColor(R.color.forestgreen), getContext().getColor(R.color.forestgreen)}; - - progressIndicator.setIndicatorColor(runningColors); - progressIndicator.setIndeterminateAnimationType( - LinearProgressIndicator.INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS); - progressIndicator.setVisibility(LinearProgressIndicator.INVISIBLE); - - editTexts = new LinkedList<>(); - editTexts.add(iperf3EtIp); - editTexts.add(iperf3EtPort); - editTexts.add(iperf3EtBandwidth); - editTexts.add(iperf3EtDuration); - editTexts.add(iperf3EtInterval); - editTexts.add(iperf3EtBytes); - editTexts.add(iperf3EtStreams); - editTexts.add(iperf3Cport); - sendBtn = v.findViewById(R.id.iperf3_send); - instancesBtn = v.findViewById(R.id.iperf3_instances_button); - - sendBtn.setOnClickListener(this::executeIperfCommand); - instancesBtn.setOnClickListener(this::showInstances); - - iperf3BiDir = v.findViewById(R.id.iperf_bidir); - iperf3Reverse = v.findViewById(R.id.iperf3_reverse); - iperf3OneOff = v.findViewById(R.id.iperf3_one_off); - - saveCheckboxInputToSharedPreferences(iperf3BiDir, IPERF3BIDIR); - saveCheckboxInputToSharedPreferences(iperf3Reverse, IPERF3REVERSE); - saveCheckboxInputToSharedPreferences(iperf3OneOff, IPERF3ONEOFF); - - protocolSpinner = v.findViewById(R.id.iperf3_protocol_spinner); - ArrayAdapter adapter = - ArrayAdapter.createFromResource(getContext(), R.array.iperf_protocol, - R.layout.support_simple_spinner_dropdown_item); - adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item); - protocolSpinner.setAdapter(adapter); - - iperf3ModeSpinner = v.findViewById(R.id.iperf3_mode_spinner); - ArrayAdapter mode_adapter = - ArrayAdapter.createFromResource(getContext(), R.array.iperf_mode, - R.layout.support_simple_spinner_dropdown_item); - adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item); - iperf3ModeSpinner.setAdapter(mode_adapter); - - - getActivity().getSupportFragmentManager() - .setFragmentResultListener("input", getViewLifecycleOwner(), - new FragmentResultListener() { - @Override - public void onFragmentResult(@NonNull String requestKey, - @NonNull Bundle result) { - - Iperf3RunResult iperf3RunResult = - db.iperf3RunResultDao().getRunResult(result.getString("uid")); - String logFileName = iperf3RunResult.input.iperf3LogFileName.split("_")[0]; - if (logFileName.equals("iperf3")) { - logFileName = ""; - } - - iperf3EtIp.setText(iperf3RunResult.input.iperf3IP); - iperf3EtPort.setText(iperf3RunResult.input.iperf3Port); - iperf3EtBandwidth.setText(iperf3RunResult.input.iperf3Bandwidth); - iperf3EtDuration.setText(iperf3RunResult.input.iperf3Duration); - iperf3EtInterval.setText(iperf3RunResult.input.iperf3Interval); - iperf3EtBytes.setText(iperf3RunResult.input.iperf3Bytes); - iperf3EtStreams.setText(iperf3RunResult.input.streams); - iperf3Cport.setText(iperf3RunResult.input.iperf3Cport); - - iperf3Reverse.setChecked(iperf3RunResult.input.iperf3Reverse); - iperf3BiDir.setChecked(iperf3RunResult.input.iperf3BiDir); - iperf3OneOff.setChecked(iperf3RunResult.input.iperf3OneOff); - protocolSpinner.setSelection(iperf3RunResult.input.iperf3IdxProtocol); - iperf3ModeSpinner.setSelection(iperf3RunResult.input.iperf3IdxMode); - - writeToSP(); - } - }); - iperf3EtIp.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3IP, null)); - iperf3EtPort.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3PORT, null)); - iperf3EtBandwidth.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3BANDWIDTH, null)); - iperf3EtDuration.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3DURATION, null)); - iperf3EtInterval.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3INTERVAL, null)); - iperf3EtBytes.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3BYTES, null)); - iperf3EtStreams.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3STREAMS, null)); - iperf3Cport.setText(spg.getSharedPreference(SPType.iperf3_sp).getString(IPERF3CPORT, null)); - - iperf3BiDir.setChecked(spg.getSharedPreference(SPType.iperf3_sp).getBoolean(IPERF3BIDIR, false)); - iperf3Reverse.setChecked(spg.getSharedPreference(SPType.iperf3_sp).getBoolean(IPERF3REVERSE, false)); - iperf3OneOff.setChecked(spg.getSharedPreference(SPType.iperf3_sp).getBoolean(IPERF3ONEOFF, false)); - protocolSpinner.setSelection(spg.getSharedPreference(SPType.iperf3_sp).getInt(IPERF3IDXPROTOCOL, 0)); - iperf3ModeSpinner.setSelection(spg.getSharedPreference(SPType.iperf3_sp).getInt(IPERF3IDXMODE, 0)); - - - try { - Os.setenv("TMPDIR", String.valueOf(getActivity().getCacheDir()), true); - } catch (ErrnoException e) { - Log.d(TAG,e.toString()); - } - return v; - } - - public void showInstances(View view) { - Bundle bundle = new Bundle(); - bundle.putStringArrayList("iperf3List", uids); - - NavController navController; - - NavHostFragment navHostFragment = - (NavHostFragment) getActivity().getSupportFragmentManager() - .findFragmentById(R.id.fragmentContainerView); - navController = navHostFragment.getNavController(); - - navController.navigate(R.id.runners_list, bundle); - } - - private boolean isModeSpinnerClient() { - String status = iperf3ModeSpinner.getSelectedItem().toString(); - return status.equals("Client"); - } - - public void executeIperfCommand(View view) { - String[] command = parseInput().split(" "); - - String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + "/omnt/iperf3LP/"; - - if(input.iperf3Json){ - try { - Files.createDirectories(Paths.get(path)); - } catch (IOException e) { - Toast.makeText(requireContext(),"Could not create Dir files!", Toast.LENGTH_SHORT).show(); - } - } - - // create the log file; - - String iperf3WorkerID = input.uuid; - - input.iperf3LineProtocolFile = path + iperf3WorkerID + ".txt"; - Data.Builder iperf3Data = new Data.Builder(); - iperf3Data.putStringArray("commands", command); - iperf3Data.putString("iperf3WorkerID", iperf3WorkerID); - iperf3Data.putString("rawIperf3file", rawIperf3file); - iperf3Data.putString("iperf3LineProtocolFile", input.iperf3LineProtocolFile); - iperf3Data.putString("measurementName", input.measurementName); - iperf3Data.putString("ip", input.iperf3IP); - iperf3Data.putString("port", input.iperf3Port); - iperf3Data.putString("bandwidth", input.iperf3Bandwidth); - iperf3Data.putString("duration", input.iperf3Duration); - iperf3Data.putString("interval", input.iperf3Interval); - iperf3Data.putString("bytes", input.iperf3Bytes); - iperf3Data.putString("protocol", protocolSpinner.getSelectedItem().toString()); - iperf3Data.putBoolean("rev", input.iperf3Reverse); - iperf3Data.putBoolean("biDir", input.iperf3BiDir); - iperf3Data.putBoolean("oneOff", input.iperf3OneOff); - iperf3Data.putString("client", iperf3ModeSpinner.getSelectedItem().toString()); - iperf3Data.putString("timestamp", input.timestamp.toString()); - iperf3Data.putString("protocol", protocolSpinner.getSelectedItem().toString()); - iperf3Data.putString("cport", input.iperf3Cport); - - ListenableFuture> status = iperf3WM.getWorkInfosByTag("iperf3Run"); - -/* try { - for (WorkInfo workInfo : status.get()) { - if (workInfo.getState().equals(WorkInfo.State.RUNNING)) { - Toast.makeText(getContext(), "iperf3 Test is running!", Toast.LENGTH_SHORT) - .show(); - return; - } - } - } catch (ExecutionException | InterruptedException e) { - Log.d(TAG,e.toString()); -; - }*/ - - uids.add(0, iperf3WorkerID); - iperf3Data.putInt("notificationID", uids.size()); - - - OneTimeWorkRequest iperf3WR = - new OneTimeWorkRequest - .Builder(Iperf3Worker.class) - .setInputData(iperf3Data.build()) - .addTag("iperf3Run") - .addTag(iperf3WorkerID) - .build(); - OneTimeWorkRequest iperf3LP = - new OneTimeWorkRequest - .Builder(Iperf3ToLineProtocolWorker.class) - .setInputData(iperf3Data.build()) - .build(); - OneTimeWorkRequest iperf3UP = - new OneTimeWorkRequest - .Builder(Iperf3UploadWorker.class) - .setInputData(iperf3Data.build()) - .addTag("iperf3") - .build(); - - iperf3RunResultDao.insert( - new Iperf3RunResult(iperf3WorkerID, -100, false, input, input.timestamp)); - - - - if (spg.getSharedPreference(SPType.logging_sp).getBoolean("enable_influx", false) && input.iperf3Json) { - iperf3WM.beginWith(iperf3WR).then(iperf3LP).then(iperf3UP).enqueue(); - } else if(input.iperf3Json) { - iperf3WM.beginWith(iperf3WR).then(iperf3LP).enqueue(); - } else { - iperf3WM.beginWith(iperf3WR).enqueue(); - } - - - - Handler progressbarHandler = new Handler(Looper.myLooper()); - - iperf3WM.getWorkInfoByIdLiveData(iperf3WR.getId()).observeForever(workInfo -> { - int iperf3_result; - iperf3_result = workInfo.getOutputData().getInt("iperf3_result", -100); - if (workInfo.getState().equals(WorkInfo.State.CANCELLED)) { - iperf3_result = -1; - } - iperf3RunResultDao.updateResult(iperf3WorkerID, iperf3_result); - Log.d(TAG, "onChanged: iperf3_result: " + iperf3_result); - if (iperf3_result == -100) { - progressIndicator.setVisibility(LinearProgressIndicator.VISIBLE); - if (!isModeSpinnerClient()) { - progressbarHandler.postDelayed(progressbarUpdate, SHOW_PROGRESSBAR); - } - } else if (iperf3_result != 0) { - progressIndicator.setIndicatorColor(failedColors); - progressbarHandler.postDelayed(progressbarUpdate, SHOW_PROGRESSBAR); - - } else { - progressIndicator.setIndicatorColor(succesColors); - progressbarHandler.postDelayed(progressbarUpdate, SHOW_PROGRESSBAR); - } - - }); - iperf3WM.getWorkInfoByIdLiveData(iperf3UP.getId()).observeForever(workInfo -> { - boolean iperf3_upload; - iperf3_upload = workInfo.getOutputData().getBoolean("iperf3_upload", false); - Log.d(TAG, "onChanged: iperf3_upload: " + iperf3_upload); - iperf3RunResultDao.updateUpload(iperf3WorkerID, iperf3_upload); - }); - - - } - - private String getKeyFromId(String s, String value) { - String key = ""; - switch (s) { - case "iperf3_logfile": - key = "--logfile"; - input.measurementName = value; - break; - case "iperf3_streams": - key = "-P"; - input.streams = value; - break; - case "iperf3_ip": - key = "-c"; - input.iperf3IP = value; - break; - case "iperf3_port": - key = "-p"; - input.iperf3Port = value; - break; - case "iperf3_bandwidth": - key = "-b"; - input.iperf3Bandwidth = value; - break; - case "iperf3_duration": - key = "-t"; - input.iperf3Duration = value; - break; - case "iperf3_interval": - key = "-i"; - input.iperf3Interval = value; - break; - case "iperf3_bytes": - key = "-n"; - input.iperf3Bytes = value; - break; - case "iperf3_cport": - key = "--cport"; - input.iperf3Cport = value; - break; - } - return key; - } - - private String parseInput() { - List stb = new LinkedList<>(); - for (EditText et : editTexts) { - String value = et.getText().toString(); - if (!value.equals("")) { - String s = getResources().getResourceEntryName(et.getId()); - String key = getKeyFromId(s, value); - if (s.equals("iperf3_bandwidth")) { - value += "M"; - } - stb.add(key); - stb.add(value); - } - } - - String protocol = protocolSpinner.getSelectedItem().toString(); - if (!protocol.equals("TCP")) { - stb.add("--" + protocol.toLowerCase()); - } - input.iperf3IdxProtocol = protocolSpinner.getSelectedItemPosition(); - - input.timestamp = new Timestamp(System.currentTimeMillis()); - String iperf3TS = input.timestamp.toString().replace(" ", "_").replace(":", "_"); - - input.uuid = UUID.randomUUID().toString(); - - input.measurementName = "Iperf3"; - this.logFileName = String.format("iperf3_%s_%s.json", iperf3TS, input.uuid); - - input.iperf3LogFileName = this.logFileName; - this.rawIperf3file = this.logFileDir + this.logFileName; - input.iperf3rawIperf3file = this.rawIperf3file; - - stb.add("--logfile"); - stb.add(this.rawIperf3file); - - input.iperf3BiDir = false; - input.iperf3Reverse = false; - input.iperf3OneOff = false; - input.iperf3Json = true; - - if (!isModeSpinnerClient()) { - stb.add("-s"); - input.iperf3IdxMode = iperf3ModeSpinner.getSelectedItemPosition(); - } - if (iperf3BiDir.isChecked()) { - stb.add("--bidir"); - input.iperf3BiDir = true; - } - if (iperf3Reverse.isChecked()) { - stb.add("--reverse"); - input.iperf3Reverse = true; - } - if (iperf3OneOff.isChecked()) { - stb.add("--one-off"); - input.iperf3OneOff = true; - } - stb.add("--json-stream"); - - stb.add("--connect-timeout"); - stb.add("500"); - - - String joined = String.join(" ", stb); - - Log.d(TAG, "parseInput: joined command " + joined); - input.iperf3Command = joined; - - - return joined; - } - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - } - - private void writeToSP() { - SharedPreferences.Editor editor = spg.getSharedPreference(SPType.iperf3_sp).edit(); - editor.putInt(IPERF3IDXPROTOCOL, protocolSpinner.getSelectedItemPosition()); - editor.putInt(IPERF3IDXMODE, iperf3ModeSpinner.getSelectedItemPosition()); - editor.putString(IPERF3IP, iperf3EtIp.getText().toString()); - editor.putString(IPERF3PORT, iperf3EtPort.getText().toString()); - editor.putString(IPERF3BANDWIDTH, iperf3EtBandwidth.getText().toString()); - editor.putString(IPERF3DURATION, iperf3EtDuration.getText().toString()); - editor.putString(IPERF3INTERVAL, iperf3EtInterval.getText().toString()); - editor.putString(IPERF3BYTES, iperf3EtBytes.getText().toString()); - editor.putString(IPERF3STREAMS, iperf3EtStreams.getText().toString()); - editor.putString(IPERF3CPORT, iperf3Cport.getText().toString()); - - editor.putBoolean(IPERF3BIDIR, iperf3BiDir.isChecked()); - editor.putBoolean(IPERF3REVERSE, iperf3Reverse.isChecked()); - editor.putBoolean(IPERF3ONEOFF, iperf3OneOff.isChecked()); - editor.apply(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if(this.isResumed()) - writeToSP(); - } - - - - public static class Iperf3Input { - public boolean iperf3BiDir; - public boolean iperf3Reverse; - public boolean iperf3Json; - public boolean iperf3OneOff; - public int iperf3IdxMode; - public int iperf3IdxProtocol; - public String uuid; - public String iperf3Command; - public String iperf3rawIperf3file; - public String iperf3LogFileName; - public String measurementName; - public String iperf3IP; - public String iperf3Port; - public String iperf3Bandwidth; - public String iperf3LineProtocolFile; - public String iperf3Duration; - public String iperf3Interval; - public String iperf3Bytes; - public Timestamp timestamp; - public String streams; - public String iperf3Cport; - private List getFields(){ - List fields = Arrays.asList(Iperf3Input.class.getDeclaredFields()); - fields.sort((o1, o2) -> { - return o1.toGenericString().compareTo(o2.toGenericString()); - }); - return fields; - } - private LinearLayout getTextView(String name, String value, Context ct){ - LinearLayout mainLL = new LinearLayout(ct); - mainLL.setOrientation(LinearLayout.HORIZONTAL); - - - LinearLayout.LayoutParams parameterLayoutName = new LinearLayout.LayoutParams( - 0, - ViewGroup.LayoutParams.WRAP_CONTENT); - parameterLayoutName.weight = 1F; - TextView parameterName = new TextView(ct); - parameterName.setTextIsSelectable(true); - parameterName.setText(String.format("%s", name)); - parameterName.setLayoutParams(parameterLayoutName); - TextView parameterValue = new TextView(ct); - parameterValue.setTextIsSelectable(true); - parameterValue.setText(String.format("%s", value)); - LinearLayout.LayoutParams parameterLayoutValue = new LinearLayout.LayoutParams( - 0, - ViewGroup.LayoutParams.WRAP_CONTENT); - parameterLayoutValue.weight = 3F; - parameterValue.setLayoutParams(parameterLayoutValue); - - mainLL.addView(parameterName); - mainLL.addView(parameterValue); - return mainLL; - } - private LinearLayout getTextViewValue(String key, String value, Context ct){ - LinearLayout mainLL = new LinearLayout(ct); - mainLL.setOrientation(LinearLayout.HORIZONTAL); - mainLL.setFocusable(false); - mainLL.setFocusedByDefault(false); - - TextView parameterValue = new TextView(ct); - - parameterValue.setTextIsSelectable(true); - parameterValue.setText(String.format("%s", value)); - LinearLayout.LayoutParams parameterLayoutValue = new LinearLayout.LayoutParams( - 0, - ViewGroup.LayoutParams.WRAP_CONTENT); - parameterValue.setPadding(5, 5, 5, 5); - parameterLayoutValue.setMargins(0, 0, 10, 10); - parameterLayoutValue.weight = 1F; - parameterValue.setLayoutParams(parameterLayoutValue); - - mainLL.addView(parameterValue); - return mainLL; - } - public LinearLayout getInputAsLinearLayoutKeyValue(LinearLayout mainLL, Context ct){ - mainLL.setOrientation(LinearLayout.VERTICAL); - LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - 0, - ViewGroup.LayoutParams.WRAP_CONTENT); - layoutParams.weight = 8F; - mainLL.setLayoutParams(layoutParams); - String[] protocol = - ct.getResources().getStringArray(R.array.iperf_protocol); - String[] mode = ct.getResources().getStringArray(R.array.iperf_mode); - for(Field parameter: getFields()){ - try { - Object parameterValueObj = parameter.get(this); - if(parameterValueObj == null){ - continue; - } - - String parameterName = parameter.getName().replace("iperf3", ""); - if(parameterName.equals("measurementName") - || parameterName.equals("rawIperf3file") - || parameterName.equals("LogFileName") - || parameterName.equals("Command") - || parameterName.equals("LineProtocolFile")) continue; - - String parameterValue = parameter.get(this).toString(); - if(parameterValue.equals("false")){ - continue; - } - if(parameterName.equals("IdxProtocol")){ - parameterName = "Protocol"; - parameterValue = protocol[Integer.parseInt(parameterValue)]; - } - - if(parameterName.equals("IdxMode")){ - parameterName = "Mode"; - parameterValue = mode[Integer.parseInt(parameterValue)]; - } - mainLL.addView(getTextView( - parameterName, - parameterValue, - ct)); - - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - return mainLL; - } - - public LinearLayout getInputAsLinearLayoutValue(LinearLayout mainLL, Context ct){ - mainLL.setOrientation(LinearLayout.HORIZONTAL); - LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - 0, - ViewGroup.LayoutParams.WRAP_CONTENT); - layoutParams.weight = 10F; - mainLL.setLayoutParams(layoutParams); - String[] protocol = - ct.getResources().getStringArray(R.array.iperf_protocol); - String[] mode = ct.getResources().getStringArray(R.array.iperf_mode); - for(Field parameter: getFields()){ - try { - Object parameterValueObj = parameter.get(this); - if(parameterValueObj == null){ - continue; - } - - String parameterName = parameter.getName().replace("iperf3", ""); - if(parameterName.equals("measurementName") - || parameterName.equals("rawIperf3file") - || parameterName.equals("LogFileName") - || parameterName.equals("Command") - || parameterName.equals("LineProtocolFile") - || parameterName.equals("timestamp") - || parameterName.equals("uuid")) continue; - - String parameterValue = parameter.get(this).toString(); - if(parameterValue.equals("false")){ - continue; - } - if(parameterName.equals("IdxProtocol")){ - parameterName = "Protocol"; - parameterValue = protocol[Integer.parseInt(parameterValue)]; - } - - if(parameterName.equals("IdxMode")){ - parameterName = "Mode"; - parameterValue = mode[Integer.parseInt(parameterValue)]; - } - - if(parameterValue.equals("true")){ - parameterValue = parameterName; - } - - mainLL.addView(getTextViewValue( - parameterName, - parameterValue, - ct)); - - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - return mainLL; - } - } -} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LibLoader.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LibLoader.java index c8d20e25..90e51528 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LibLoader.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LibLoader.java @@ -13,7 +13,8 @@ import java.util.ArrayList; import java.util.List; -public class Iperf3LibLoader { +public class +Iperf3LibLoader { private static boolean done = false; private static final String TAG = "Iperf3LibLoader"; @@ -24,10 +25,11 @@ public class Iperf3LibLoader { "iperf3.12", "iperf3.15", "iperf3.16", - "iperf3.17.1" + "iperf3.17.1", + "iperf3.18" ); - protected static synchronized void load() { + public static synchronized void load() { if (done) { return; } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ListFragment.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ListFragment.java deleted file mode 100644 index 6ae30a5d..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ListFragment.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Peter Hasse - * SPDX-FileCopyrightText: 2023 Johann Hackler - * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS - * - * SPDX-License-Identifier: BSD-3-Clause-Clear - */ - -//from https://codeburst.io/android-swipe-menu-with-recyclerview-8f28a235ff28 - -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; - -import android.annotation.SuppressLint; -import android.graphics.Canvas; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - -import com.google.android.material.floatingactionbutton.FloatingActionButton; - -import java.util.ArrayList; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.SwipeController; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.SwipeControllerActions; - - -public class Iperf3ListFragment extends Fragment { - private final String TAG = "Iperf3ListFragment"; - private SwipeController swipeController = null; - private RecyclerView recyclerView; - private Iperf3RecyclerViewAdapter adapter; - private LinearLayoutManager linearLayoutManager; - private FloatingActionButton uploadBtn; - private Iperf3ResultsDataBase db; - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @SuppressLint("NotifyDataSetChanged") - public void updateIperf3ListAdapter() { - if (this.adapter != null) { - this.adapter.notifyDataSetChanged(); - } - } - - @SuppressLint("ClickableViewAccessibility") - public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_iperf3_list, parent, false); - ArrayList uids = this.getArguments().getStringArrayList("iperf3List"); - recyclerView = v.findViewById(R.id.runners_list); - uploadBtn = v.findViewById(R.id.iperf3_upload_button); - linearLayoutManager = - new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); - recyclerView.setLayoutManager(linearLayoutManager); - adapter = new Iperf3RecyclerViewAdapter(getActivity(), uids, uploadBtn); - recyclerView.setAdapter(adapter); - db = Iperf3ResultsDataBase.getDatabase(requireContext()); - - - swipeController = new SwipeController(new SwipeControllerActions() { - @SuppressLint("NotifyDataSetChanged") - @Override - public void onRightClicked(int position) { - WorkManager iperf3WM = WorkManager.getInstance(getContext()); - String uid = uids.get(position); - iperf3WM.cancelAllWorkByTag(uid); - Iperf3RunResultDao iperf3RunResultDao = Iperf3ResultsDataBase.getDatabase(requireContext()).iperf3RunResultDao(); - Iperf3RunResult runResult = iperf3RunResultDao.getRunResult(uid); - - - Data.Builder iperf3Data = new Data.Builder(); - - iperf3Data.putString("rawIperf3file", runResult.input.iperf3rawIperf3file); - iperf3Data.putString("iperf3LineProtocolFile", runResult.input.iperf3LineProtocolFile); - iperf3Data.putString("measurementName", runResult.input.measurementName); - iperf3Data.putString("ip", runResult.input.iperf3IP); - iperf3Data.putString("port", runResult.input.iperf3Port); - iperf3Data.putString("bandwidth", runResult.input.iperf3Bandwidth); - iperf3Data.putString("duration", runResult.input.iperf3Duration); - iperf3Data.putString("interval", runResult.input.iperf3Interval); - iperf3Data.putString("bytes", runResult.input.iperf3Bytes); - iperf3Data.putString("protocol", Iperf3Utils.getProtocolString(runResult.input.iperf3IdxProtocol)); - iperf3Data.putBoolean("rev", runResult.input.iperf3Reverse); - iperf3Data.putBoolean("biDir", runResult.input.iperf3BiDir); - iperf3Data.putBoolean("oneOff", runResult.input.iperf3OneOff); - iperf3Data.putString("client", Iperf3Utils.getModeString(runResult.input.iperf3IdxMode)); - iperf3Data.putString("timestamp", runResult.input.timestamp.toString()); - - OneTimeWorkRequest iperf3LP = - new OneTimeWorkRequest - .Builder(Iperf3ToLineProtocolWorker.class) - .setInputData(iperf3Data.build()) - .build(); - iperf3WM.enqueue(iperf3LP); - - - if(runResult.result != 0) - iperf3RunResultDao.updateResult(uid, 1); - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - adapter.notifyDataSetChanged(); - } - }, 100); - } - - @Override - public void onLeftClicked(int position) { - Bundle input = new Bundle(); - input.putString("uid", uids.get(position)); - getActivity().getSupportFragmentManager().setFragmentResult("input", input); - getActivity().getSupportFragmentManager().popBackStack(); - } - }); - - - ItemTouchHelper itemTouchhelper = new ItemTouchHelper(swipeController); - itemTouchhelper.attachToRecyclerView(recyclerView); - - recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { - @Override - public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - swipeController.onDraw(c); - } - }); - return v; - } - - @Override - public void onViewCreated(View v, Bundle savedInstanceState) { - super.onViewCreated(v, savedInstanceState); - } - - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - } -} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LogFragment.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LogFragment.java deleted file mode 100644 index d6364041..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3LogFragment.java +++ /dev/null @@ -1,367 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Peter Hasse - * SPDX-FileCopyrightText: 2023 Johann Hackler - * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS - * - * SPDX-License-Identifier: BSD-3-Clause-Clear - */ - -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; - -import androidx.cardview.widget.CardView; -import androidx.core.widget.TextViewCompat; -import androidx.fragment.app.Fragment; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Error; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.SUM_TYPE; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.Sum; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.UDP.UDP_DL_SUM; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.METRIC_TYPE; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.Metric; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; - -public class Iperf3LogFragment extends Fragment { - - private static final String TAG = "Iperf3LogFragment"; - private View v; - private Iperf3ResultsDataBase db; - private Handler logHandler; - private File file; - private String uid; - private Drawable runIcon; - private Drawable uploadIcon; - private ImageView runIconView; - private ImageView uploadIconView; - - private TextView iperf3OutputViewer; - private LinearLayout parameterLL; - private Context ct; - private LinearLayout metricLL; - private Metric defaultReverseThroughput; - private Metric defaultThroughput; - private Metric defaultRTT; - private Metric defaultJITTER; - private Metric PACKET_LOSS; - - public Iperf3LogFragment() { - } - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.db = Iperf3ResultsDataBase.getDatabase(getActivity().getApplicationContext()); - } - - private void setFields(Iperf3RunResult iperf3RunResult) { - - } - private final Runnable logUpdate = new Runnable() { - @Override - public void run() { - Iperf3RunResult iperf3RunResult = db.iperf3RunResultDao().getRunResult(uid); - Log.d(TAG, "run: " + iperf3RunResult.result); - runIcon = Iperf3Utils.getDrawableResult(requireContext(), iperf3RunResult.result); - runIconView.setImageDrawable(runIcon); - uploadIcon = Iperf3Utils.getDrawableUpload(ct, iperf3RunResult.result, iperf3RunResult.uploaded); - uploadIconView.setImageDrawable(uploadIcon); - - BufferedReader br = null; - StringBuilder text = new StringBuilder(); - - try { - br = new BufferedReader(new FileReader(file)); - } catch (FileNotFoundException e) { - iperf3OutputViewer.setText(String.format("no iPerf3 file found, with following path: \n %s", iperf3RunResult.input.iperf3rawIperf3file)); - logHandler.removeCallbacks(logUpdate); - return; - } - String line; - - Iperf3Parser iperf3Parser = new Iperf3Parser(iperf3RunResult.input.iperf3rawIperf3file); - iperf3Parser.addPropertyChangeListener(new PropertyChangeListener() { - - private void parseSum(Sum sum, Metric throughput){ - SUM_TYPE sumType = sum.getSumType(); - throughput.update(sum.getBits_per_second()); - switch (sumType){ - case UDP_DL: - defaultJITTER.update(((UDP_DL_SUM)sum).getJitter_ms()); - PACKET_LOSS.update(((UDP_DL_SUM) sum).getLost_percent()); - case TCP_DL: - if(throughput.getDirectionName().getText().equals("Throughput")){ - throughput.getDirectionName().setText("Downlink Mbit/s"); - } - break; - case UDP_UL: - case TCP_UL: - if(throughput.getDirectionName().getText().equals("Throughput")){ - throughput.getDirectionName().setText("Uplink Mbit/s"); - } - break; - } - - } - public void propertyChange(PropertyChangeEvent evt) { - - switch (evt.getPropertyName()){ - case "interval": - Interval interval = (Interval) evt.getNewValue(); - parseSum(interval.getSum(), defaultThroughput); - if(interval.getSumBidirReverse() != null) parseSum(interval.getSumBidirReverse(), - defaultReverseThroughput); - break; - case "start": - break; - case "end": - break; - case "error": - Error error = (Error) evt.getNewValue(); - TextView errorView = new TextView(ct); - errorView.setText(error.getError()); - errorView.setTextColor(ct.getColor(R.color.crimson)); - errorView.setPadding(10, 10, 10, 10); - errorView.setTextSize(20); - metricLL.addView(errorView); - break; - } - } - }); - - iperf3Parser.parse(); - if (iperf3RunResult.result != -100) { - logHandler.removeCallbacks(logUpdate); - return; - } - setFields(iperf3RunResult); - logHandler.removeCallbacks(logUpdate); - logHandler.postDelayed(this, 1000); - } - }; - - - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - v = inflater.inflate(R.layout.fragment_iperf3_log, container, false); - Iperf3RunResult iperf3RunResult = - db.iperf3RunResultDao().getRunResult(this.getArguments().getString("uid")); - ct = requireContext(); - LinearLayout mainLL = v.findViewById(R.id.iperf3_list_fragment); - mainLL.setOrientation(LinearLayout.VERTICAL); - - LinearLayout firstRow = new LinearLayout(ct); - firstRow.setOrientation(LinearLayout.HORIZONTAL); - - LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - mainLL.setLayoutParams(layoutParams); - - LinearLayout.LayoutParams foo = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - foo.setMargins(30, 10, 0, 0); - firstRow.setLayoutParams(foo); - - - runIconView = new ImageView(ct); - LinearLayout.LayoutParams runIconViewLayout = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT); - runIconViewLayout.weight = 0.2F; - runIconViewLayout.height = 100; - runIconViewLayout.gravity = Gravity.RIGHT; - runIconView.setLayoutParams(runIconViewLayout); - runIcon = Iperf3Utils.getDrawableResult(ct, iperf3RunResult.result); - runIconView.setImageDrawable(runIcon); - runIconView.setTooltipText(String.format("Indicates the result of the iPerf3 run: %s", iperf3RunResult.result)); - runIconView.setLayoutParams(runIconViewLayout); - - - uploadIconView = new ImageView(ct); - uploadIconView.setTooltipText(String.format("Indicates if the iPerf3 run was uploaded to the server: %s", iperf3RunResult.uploaded)); - LinearLayout.LayoutParams uploadIconViewLayout = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT); - uploadIconViewLayout.weight = 0.2F; - uploadIconViewLayout.height = 100; - uploadIconViewLayout.gravity = Gravity.RIGHT; - uploadIcon = Iperf3Utils.getDrawableUpload(ct, iperf3RunResult.result, iperf3RunResult.uploaded); - uploadIconView.setImageDrawable(uploadIcon); - uploadIconView.setLayoutParams(uploadIconViewLayout); - - - parameterLL = iperf3RunResult.input.getInputAsLinearLayoutKeyValue(new LinearLayout(ct), ct); - - - - firstRow.addView(parameterLL); - - - - - LinearLayout headerWrapper = new LinearLayout(ct); - LinearLayout.LayoutParams headerWrapperLayout = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - headerWrapper.setLayoutParams(headerWrapperLayout); - headerWrapper.setOrientation(LinearLayout.VERTICAL); - CardView cardView = new CardView(ct); - LinearLayout.LayoutParams cardViewLayout = Iperf3Utils.getLayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - 0); - headerWrapper.setPadding(30, 30, 30, 30); - cardView.setLayoutParams(cardViewLayout); - cardView.setRadius(10); - cardView.setCardElevation(10); - cardView.setUseCompatPadding(true); - cardView.addView(headerWrapper); - cardView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - //TODO: implement click on card, goto input fragmet - } - }); - - LinearLayout header = new LinearLayout(ct); - LinearLayout.LayoutParams headerLayout = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, 100); - header.setLayoutParams(headerLayout); - header.setOrientation(LinearLayout.HORIZONTAL); - - TextView headerName = new TextView(ct); - headerName.setText("iPerf3 run"); - LinearLayout.LayoutParams headerNameLayout = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT); - headerNameLayout.weight = 0.6F; - headerName.setLayoutParams(headerNameLayout); - header.addView(headerName); - header.addView(runIconView); - header.addView(uploadIconView); - ImageButton expandButton = new ImageButton(ct); - expandButton.setImageResource(R.drawable.baseline_expand_more_24); - firstRow.setVisibility(View.GONE); - expandButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - if (firstRow.getVisibility() == View.VISIBLE) { - firstRow.setVisibility(View.GONE); - expandButton.setImageResource(R.drawable.baseline_expand_more_24); - } else { - firstRow.setVisibility(View.VISIBLE); - expandButton.setImageResource(R.drawable.baseline_expand_less_24); - } - - } - }); - LinearLayout.LayoutParams imageButtonLayout = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT); - imageButtonLayout.weight = 0.2F; - expandButton.setLayoutParams(imageButtonLayout); - - header.addView(expandButton); - headerWrapper.addView(header); - headerWrapper.addView(firstRow); - - uid = iperf3RunResult.uid; - - mainLL.addView(cardView); - - LinearLayout secondRow = new LinearLayout(ct); - LinearLayout.LayoutParams secondRowLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); - secondRow.setOrientation(LinearLayout.HORIZONTAL); - secondRowLayoutParams.setMargins(0, 20, 0, 20); - secondRow.setLayoutParams(secondRowLayoutParams); - ScrollView scrollView = new ScrollView(ct); - iperf3OutputViewer = new TextView(ct); - iperf3OutputViewer.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(iperf3OutputViewer, - 1, 10, 1, - TypedValue.COMPLEX_UNIT_SP); - iperf3OutputViewer.setTextIsSelectable(true); - - metricLL = new LinearLayout(ct); - metricLL.setOrientation(LinearLayout.VERTICAL); - metricLL.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - - - - defaultThroughput = new Metric(METRIC_TYPE.THROUGHPUT, ct); - defaultReverseThroughput = new Metric(METRIC_TYPE.THROUGHPUT, ct); - - metricLL.addView(defaultThroughput.createMainLL("Throughput")); - - if(iperf3RunResult.input.iperf3BiDir) { - metricLL.addView(defaultReverseThroughput.createMainLL("Throughput")); - if(iperf3RunResult.input.iperf3IdxProtocol == 0) { - //defaultRTT = new Metric(METRIC_TYPE.RTT); - //metricLL.addView(defaultRTT.createOneDirection("RTT")); - } - if(iperf3RunResult.input.iperf3IdxProtocol == 1) { - defaultJITTER = new Metric(METRIC_TYPE.JITTER, ct); - metricLL.addView(defaultJITTER.createMainLL("Jitter ms")); - PACKET_LOSS = new Metric(METRIC_TYPE.PACKET_LOSS, ct); - metricLL.addView(PACKET_LOSS.createMainLL("Packet Loss %")); - } - } - if(iperf3RunResult.input.iperf3Reverse) { - if(iperf3RunResult.input.iperf3IdxProtocol == 1) { - defaultJITTER = new Metric(METRIC_TYPE.JITTER, ct); - metricLL.addView(defaultJITTER.createMainLL("Jitter ms")); - PACKET_LOSS = new Metric(METRIC_TYPE.JITTER, ct); - metricLL.addView(PACKET_LOSS.createMainLL("Packet Loss %")); - } - } else if(!iperf3RunResult.input.iperf3BiDir) { - if(iperf3RunResult.input.iperf3IdxProtocol == 0) { - //defaultRTT = new Metric(METRIC_TYPE.RTT); - //metricLL.addView(defaultRTT.createOneDirection("RTT ms")); - } - } - - mainLL.addView(metricLL); - - - - - mainLL.addView(secondRow); - if(iperf3RunResult.input.iperf3rawIperf3file == null){ - iperf3OutputViewer.setText("iPerf3 file path empty!"); - return v; - } - file = new File(iperf3RunResult.input.iperf3rawIperf3file); - - - logHandler = new Handler(Looper.myLooper()); - logHandler.post(logUpdate); - return v; - } - - public void onPause() { - super.onPause(); - if(logHandler != null) logHandler.removeCallbacks(logUpdate); - } - - -} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Parser.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Parser.java deleted file mode 100644 index 781ade6b..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Parser.java +++ /dev/null @@ -1,88 +0,0 @@ -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; - -import org.json.JSONObject; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Error; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.start.Start; - - -public class Iperf3Parser { - - private final String pathToFile; - private final File file; - private BufferedReader br = null; - private PropertyChangeSupport support; - private Start start; - private final Intervals intervals = new Intervals(); - Iperf3Parser(String pathToFile) { - this.pathToFile = pathToFile; - this.file = new File(this.pathToFile); - try { - br = new BufferedReader(new FileReader(file)); - } catch (FileNotFoundException ex) { - System.out.println("File not found"); - return; - } - this.support = new PropertyChangeSupport(this); - } - - public void parse(){ - String line; - try { - while ((line = br.readLine()) != null) { - JSONObject obj = new JSONObject(line); - String event = obj.getString("event"); - switch (event) { - case "start": - start = new Start(); - JSONObject startData = obj.getJSONObject("data"); - start.parseStart(startData); - break; - case "interval": - Interval interval = new Interval(); - JSONObject intervalData = obj.getJSONObject("data"); - interval.parse(intervalData); - support.firePropertyChange("interval", null, interval); - intervals.addInterval(interval); - break; - case "end": - System.out.println("End"); - break; - case "error": - Error error = new Error(); - String errorString = obj.getString("data"); - error.parse(errorString); - support.firePropertyChange("error", null, error); - break; - default: - System.out.println("Unknown event"); - break; - } - } - } catch (Exception e) { - System.out.println("Error reading file"); - } - } - - public Intervals getIntervals() { - return intervals; - } - public Start getStart() { - return start; - } - public void addPropertyChangeListener(PropertyChangeListener pcl) { - support.addPropertyChangeListener(pcl); - } - - public void removePropertyChangeListener(PropertyChangeListener pcl) { - support.removePropertyChangeListener(pcl); - } -} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RecyclerViewAdapter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RecyclerViewAdapter.java index 0ab33dce..089cbab3 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RecyclerViewAdapter.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RecyclerViewAdapter.java @@ -8,60 +8,67 @@ package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; -import android.annotation.SuppressLint; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Configuration; -import android.os.Bundle; -import android.util.Log; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.core.content.ContextCompat; -import androidx.fragment.app.FragmentActivity; -import androidx.preference.PreferenceManager; +import androidx.lifecycle.Observer; import androidx.recyclerview.widget.RecyclerView; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; +import com.google.android.flexbox.FlexboxLayout; +import com.google.android.material.button.MaterialButton; +import com.google.android.material.card.MaterialCardView; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.progressindicator.LinearProgressIndicator; + +import org.w3c.dom.Text; import java.util.ArrayList; import java.util.HashMap; -import java.util.Map; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; +import java.util.Objects; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3ResultsDataBase; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResult; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResultDao; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.METRIC_TYPE; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricCalculator; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricView; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; public class Iperf3RecyclerViewAdapter extends RecyclerView.Adapter { private final String TAG = "Iperf3RecyclerViewAdapter"; private final Iperf3ResultsDataBase db; - private final ArrayList uids; private Context context; - private final FragmentActivity c; private final HashMap selectedRuns; private final HashMap selectedCardViews; private final FloatingActionButton uploadBtn; + private Iperf3RunResultDao iperf3RunResultDao; + private Observer observer; + private String selectedUUID; + public Iperf3RecyclerViewAdapter(FloatingActionButton uploadBtn) { - public Iperf3RecyclerViewAdapter(FragmentActivity c, ArrayList uids, - FloatingActionButton uploadBtn) { - this.c = c; - this.uids = uids; this.db = Iperf3ResultsDataBase.getDatabase(context); + this.iperf3RunResultDao = db.iperf3RunResultDao(); + this.selectedRuns = new HashMap<>(); this.selectedCardViews = new HashMap<>(); this.uploadBtn = uploadBtn; - this.uploadBtn.setOnClickListener(new View.OnClickListener() { + + /* this.uploadBtn.setOnClickListener(new View.OnClickListener() { @SuppressLint("NotifyDataSetChanged") @Override public void onClick(View view) { @@ -83,7 +90,7 @@ public void onClick(View view) { Iperf3RunResult runResult = iperf3RunResultDao.getRunResult(uid); Data.Builder data = new Data.Builder(); - data.putString("iperf3LineProtocolFile", runResult.input.iperf3LineProtocolFile); + data.putString("iperf3LineProtocolFile", runResult.input.getParameter().getLineProtocolFile()); OneTimeWorkRequest iperf3UP = new OneTimeWorkRequest.Builder(Iperf3UploadWorker.class) .setInputData(data.build()) @@ -95,7 +102,7 @@ public void onClick(View view) { boolean iperf3_upload; iperf3_upload = workInfo.getOutputData().getBoolean("iperf3_upload", false); if (iperf3_upload) { - iperf3RunResultDao.updateUpload(uid, iperf3_upload); + //iperf3RunResultDao.updateUpload(uid, iperf3_upload); selectedRuns.remove(stringIntegerEntry.getKey()); notifyItemChanged(stringIntegerEntry.getValue()); } @@ -108,7 +115,7 @@ public void onClick(View view) { } }); - +*/ } @@ -118,7 +125,7 @@ public Iperf3RecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGrou int viewType) { context = parent.getContext(); LayoutInflater inflater = LayoutInflater.from(context); - CardView v = (CardView) inflater.inflate(R.layout.fragment_iperf3_row_item, parent, false); + MaterialCardView v = (MaterialCardView) inflater.inflate(R.layout.fragment_iperf3_row_item, parent, false); v.setFocusable(false); v.setClickable(false); ViewHolder viewHolder = new ViewHolder(v); @@ -126,6 +133,10 @@ public Iperf3RecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGrou return viewHolder; } + public String getSelectedUUID(){ + return selectedUUID; + } + public boolean isNightMode(Context context) { int nightModeFlags = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; return nightModeFlags == Configuration.UI_MODE_NIGHT_YES; @@ -133,145 +144,268 @@ public boolean isNightMode(Context context) { @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - holder.iPerf3Parameters.removeAllViews(); Iperf3RunResult test = getItemByPosition(position); - holder.itemView.setTag(uids.get(position)); - if (selectedRuns.containsKey(test.uid)) { - holder.itemView.setBackgroundColor( - ContextCompat.getColor(context, R.color.forestgreen)); + holder.metricViewDL.setVisibility(GONE); + holder.metricViewUL.setVisibility(GONE); + + holder.metricViewUL.setMetricCalculator(test.metricUL == null ? new MetricCalculator(METRIC_TYPE.THROUGHPUT) : test.metricUL); + holder.metricViewDL.setMetricCalculator(test.metricDL == null ? new MetricCalculator(METRIC_TYPE.THROUGHPUT) : test.metricDL); + + holder.itemView.setTag(iperf3RunResultDao.getIDs().get(position)); + + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // selectedUUID = test.uid; + // notifyDataSetChanged(); + } + }); + if(Objects.equals(selectedUUID, test.uid)){ + holder.itemView.setBackgroundColor(Color.parseColor("#567845")); + } + String host = String.valueOf(test.input.getParameter().getHost()); + String dstPort = String.valueOf(test.input.getParameter().getPort()); + String bandwidth = String.valueOf(test.input.getParameter().getBitrate()); + String duration = String.valueOf(test.input.getParameter().getTime()); + String interval = String.valueOf(test.input.getParameter().getInterval()); + String bytes = String.valueOf(test.input.getParameter().getBytes()); + String streams = String.valueOf(test.input.getParameter().getParallel()); + String srcPort = String.valueOf(test.input.getParameter().getCport()); + + String mode = String.valueOf(test.input.getParameter().getMode()); + String protocol = String.valueOf(test.input.getParameter().getProtocol()); + String direction = String.valueOf(test.input.getParameter().getDirection()); + + ((TextView) holder.host.findViewById(R.id.text_parameter)).setText(host); + ((TextView) holder.dstPort.findViewById(R.id.text_parameter)).setText(dstPort); + ((TextView) holder.bandwidth.findViewById(R.id.text_parameter)).setText(bandwidth); + ((TextView) holder.duration.findViewById(R.id.text_parameter)).setText(duration); + ((TextView) holder.interval.findViewById(R.id.text_parameter)).setText(interval); + ((TextView) holder.bytes.findViewById(R.id.text_parameter)).setText(bytes); + ((TextView) holder.streams.findViewById(R.id.text_parameter)).setText(streams); + ((TextView) holder.srcPort.findViewById(R.id.text_parameter)).setText(srcPort); + + ((TextView) holder.mode.findViewById(R.id.text_parameter)).setText(mode); + ((TextView) holder.protocol.findViewById(R.id.text_parameter)).setText(protocol); + ((TextView) holder.direction.findViewById(R.id.text_parameter)).setText(direction); + holder.linearProgressIndicator.setIndicatorColor(Color.CYAN); + switch (test.result){ + case 0: + holder.linearProgressIndicator.setIndicatorColor(Color.GREEN); + holder.linearProgressIndicator.setProgress(1); + holder.linearProgressIndicator.setMax(1); + break; + case -1: + holder.linearProgressIndicator.setMax(1); + holder.linearProgressIndicator.setProgress(1); + holder.linearProgressIndicator.setIndicatorColor(Color.RED); + break; + case -100: + holder.linearProgressIndicator.setMax(test.input.getParameter().getTime()-1); + ArrayList intervals = new ArrayList(); + if(test.intervals != null) + intervals = test.intervals.getIntervalArrayList(); + int progress = intervals.size(); + holder.linearProgressIndicator.setProgress(progress); + break; + + } + + if(!dstPort.isEmpty() || dstPort.equals("")){ + holder.dstPort.setVisibility(GONE); + } else { + holder.dstPort.setVisibility(VISIBLE); + } + if(!bandwidth.isEmpty() || bandwidth.equals("")) { + holder.bandwidth.setVisibility(GONE); + } else { + holder.bandwidth.setVisibility(VISIBLE); + } + if(!duration.isEmpty() || duration.equals("")) { + holder.duration.setVisibility(GONE); + } else { + holder.duration.setVisibility(VISIBLE); + } + if(!interval.isEmpty() || interval.equals("")){ + holder.interval.setVisibility(GONE); } else { - if(isNightMode(context)) - holder.itemView.setBackgroundColor( - ContextCompat.getColor(context, R.color.cardview_dark_background)); - else - holder.itemView.setBackgroundColor( - ContextCompat.getColor(context, R.color.ic_launcher_background)); + holder.interval.setVisibility(VISIBLE); + } + if(!bytes.isEmpty() || bytes.equals("")) { + holder.bytes.setVisibility(GONE); + } else { + holder.bytes.setVisibility(VISIBLE); + } + if(!streams.isEmpty() || streams.equals("")){ + holder.streams.setVisibility(GONE); + } else { + holder.streams.setVisibility(VISIBLE); + } + if(!srcPort.isEmpty() || srcPort.equals("")) { + holder.srcPort.setVisibility(GONE); + } else { + holder.srcPort.setVisibility(VISIBLE); + } + + switch (test.input.getParameter().getDirection()){ + case BIDIR: + holder.metricViewUL.setVisibility(VISIBLE); + holder.metricViewDL.setVisibility(VISIBLE); + if(holder.metricViewUL.getMetricCalculator() != null || holder.metricViewDL.getMetricCalculator() != null){ + holder.metricViewUL.getMetricCalculator().calcAll(); + holder.metricViewDL.getMetricCalculator().calcAll(); + holder.metricViewUL.update(); + holder.metricViewDL.update(); + } + break; + case UP: + holder.metricViewUL.setVisibility(VISIBLE); + holder.metricViewDL.setVisibility(GONE); + if(holder.metricViewUL.getMetricCalculator() != null){ + holder.metricViewUL.getMetricCalculator().calcAll(); + holder.metricViewUL.update(); + } + break; + case DOWN: + holder.metricViewUL.setVisibility(GONE); + holder.metricViewDL.setVisibility(VISIBLE); + if(holder.metricViewDL.getMetricCalculator() != null){ + holder.metricViewDL.getMetricCalculator().calcAll(); + holder.metricViewDL.update(); + } + break; + } + holder.errorView.setVisibility(GONE); + if(test.result == -1) { + holder.metricViewUL.setVisibility(GONE); + holder.metricViewDL.setVisibility(GONE); + String errorText = "Error!"; + test = iperf3RunResultDao.getRunResult(test.uid); + if(test.error != null){ + errorText = test.error.getError(); + } + holder.errorView.setText(errorText); + holder.errorView.setVisibility(VISIBLE); } - holder.measurement.setText("iPerf3"); - holder.timestamp.setText(test.input.timestamp.toString()); - holder.runIcon.setImageDrawable(Iperf3Utils.getDrawableResult(context, test.result)); - holder.uploadIcon.setImageDrawable(Iperf3Utils.getDrawableUpload(context, test.result, test.uploaded)); - holder.iPerf3Parameters = test.input.getInputAsLinearLayoutValue(holder.iPerf3Parameters, context); } private Iperf3RunResult getItemByPosition(int position) { - return this.db.iperf3RunResultDao().getRunResult(this.uids.get(position)); + return iperf3RunResultDao.getRunResult(iperf3RunResultDao.getIDs().get(position)); } @Override public int getItemCount() { - return uids.size(); + return iperf3RunResultDao.getIDs().size(); } - public class ViewHolder extends RecyclerView.ViewHolder { - public TextView measurement; - public TextView timestamp; - public TextView iperf3State; - public ImageView runIcon; - public ImageView uploadIcon; - private final LinearLayout linearLayout; - private LinearLayout iPerf3Parameters; - - private LinearLayout firstRow(LinearLayout ll){ - measurement.setLayoutParams( - Iperf3Utils.getLayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 8F)); + private void setSelectedUUID(String selectedUUID) { + this.selectedUUID = selectedUUID; + } - runIcon.setLayoutParams( - Iperf3Utils.getLayoutParams(0, 80, 1F)); - - uploadIcon.setLayoutParams( - Iperf3Utils.getLayoutParams(0, 80, 1F)); - + public class ViewHolder extends RecyclerView.ViewHolder { + private FlexboxLayout parameterFlexBoxLayout; + private LinearLayout host; + private LinearLayout dstPort; + private LinearLayout bandwidth; + private LinearLayout duration; + private LinearLayout interval; + private LinearLayout bytes; + private LinearLayout streams; + private LinearLayout srcPort; + private LinearLayout mode; + private LinearLayout protocol; + private LinearLayout direction; + private LayoutInflater li; + private LinearProgressIndicator linearProgressIndicator; + private MetricView metricViewDL; + private MetricView metricViewUL; + private TextView errorView; + private LinearLayout metricLL; + private CardView cardViewError; + private MaterialButton cancel; + private MaterialButton rerun; + private void setupParameterFlexBox(){ + parameterFlexBoxLayout = itemView.findViewById(R.id.parameter_iperf3_fl); + + host = (LinearLayout) li.inflate(R.layout.parameter_view, null); + dstPort = (LinearLayout) li.inflate(R.layout.parameter_view, null); + dstPort = (LinearLayout) li.inflate(R.layout.parameter_view, null); + bandwidth = (LinearLayout) li.inflate(R.layout.parameter_view, null); + duration = (LinearLayout) li.inflate(R.layout.parameter_view, null); + interval = (LinearLayout) li.inflate(R.layout.parameter_view, null); + bytes = (LinearLayout) li.inflate(R.layout.parameter_view, null); + streams = (LinearLayout) li.inflate(R.layout.parameter_view, null); + srcPort = (LinearLayout) li.inflate(R.layout.parameter_view, null); + + mode = (LinearLayout) li.inflate(R.layout.parameter_view, null); + protocol = (LinearLayout) li.inflate(R.layout.parameter_view, null); + direction = (LinearLayout) li.inflate(R.layout.parameter_view, null); + + parameterFlexBoxLayout.addView(host); + parameterFlexBoxLayout.addView(dstPort); + parameterFlexBoxLayout.addView(bandwidth); + parameterFlexBoxLayout.addView(duration); + parameterFlexBoxLayout.addView(interval); + parameterFlexBoxLayout.addView(bytes); + parameterFlexBoxLayout.addView(streams); + parameterFlexBoxLayout.addView(srcPort); + parameterFlexBoxLayout.addView(mode); + parameterFlexBoxLayout.addView(protocol); + parameterFlexBoxLayout.addView(direction); - ll.setOrientation(LinearLayout.HORIZONTAL); - ll.addView(measurement); - ll.addView(runIcon); - ll.addView(uploadIcon); - return ll; } - private LinearLayout secondRow(LinearLayout ll){ - ll.setOrientation(LinearLayout.HORIZONTAL); - timestamp.setLayoutParams( - Iperf3Utils.getLayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1F)); - ll.addView(timestamp); - return ll; + public void setupLinearProgressIndicator(){ + linearProgressIndicator = itemView.findViewById(R.id.progress_indicator); } - private LinearLayout thirdRow(LinearLayout ll){ - ll.setOrientation(LinearLayout.HORIZONTAL); - ll.addView(iPerf3Parameters); - iPerf3Parameters.setLayoutParams( - Iperf3Utils.getLayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 0.1F)); - return ll; + public void setupMetricLinearLayout(){ + metricLL = itemView.findViewById(R.id.metrics_iperf3_ll); + metricViewDL = new MetricView(context); + metricViewUL = new MetricView(context); + metricViewDL.setup("Download [Mbit/s]"); + metricViewUL.setup("Upload [Mbit/s]"); + + errorView = new TextView(context); + errorView.setTextColor(context.getColor(R.color.material_dynamic_neutral0)); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + errorView.setLayoutParams(params); + + cardViewError = new CardView(context); + LinearLayout.LayoutParams cardViewParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + cardViewParams.setMargins(10, 10, 10, 10); + errorView.setPadding(10, 10, 10, 10); + cardViewError.setLayoutParams(cardViewParams); + GradientDrawable gd = new GradientDrawable(); + gd.setColor(context.getColor( R.color.material_dynamic_primary100)); + gd.setCornerRadius(10); +// gd.setStroke(2, 0xFF000000); + cardViewError.setBackground(gd); + + + + cardViewError.addView(errorView); + metricLL.addView(cardViewError); + metricLL.addView(metricViewDL); + metricLL.addView(metricViewUL); + errorView.setVisibility(GONE); } public ViewHolder(View itemView) { super(itemView); - Log.d(TAG, "ViewHolder: " + itemView); - measurement = new TextView(context); - timestamp = new TextView(context); - iperf3State = new TextView(context); - runIcon = new ImageView(context); - runIcon.setPadding(0, 10, 0, 0); - uploadIcon = new ImageView(context); - uploadIcon.setPadding(0, 10, 0, 0); - timestamp = new TextView(context); - iPerf3Parameters = new LinearLayout(context); - linearLayout = itemView.findViewById(R.id.iperf3_main_layout); - linearLayout.setOrientation(LinearLayout.VERTICAL); - linearLayout.addView(firstRow(new LinearLayout(context))); - linearLayout.addView(secondRow(new LinearLayout(context))); - linearLayout.addView(thirdRow(new LinearLayout(context))); - itemView.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - int viewPosition = getLayoutPosition(); - Log.d(TAG, "onLongClick: " + v.toString()); - Log.d(TAG, "onLongClick: " + viewPosition); - if (!selectedRuns.isEmpty()) { - return true; - } - if (viewPosition == RecyclerView.NO_POSITION) { - return false; - } - String uid = uids.get(viewPosition); - selectedRuns.put(uid, viewPosition); - notifyItemChanged(viewPosition); - uploadBtn.setVisibility(View.VISIBLE); - return true; - } - }); - itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int viewPosition = getLayoutPosition(); - String uid = uids.get(viewPosition); - if (!selectedRuns.isEmpty() && selectedRuns.containsKey(uid)) { - if (selectedRuns.size() == 1 && selectedRuns.containsKey(uid)) { - uploadBtn.setVisibility(View.INVISIBLE); - } - selectedRuns.remove(uid); - notifyItemChanged(viewPosition); - return; - } else if (!selectedRuns.isEmpty()) { - selectedRuns.put(uid, viewPosition); - notifyItemChanged(viewPosition); - uploadBtn.setVisibility(View.VISIBLE); - return; - } - Log.d(TAG, "onCreateView: CLICKED!"); - Bundle bundle = new Bundle(); - bundle.putString("uid", uid); - Iperf3LogFragment test = new Iperf3LogFragment(); - test.setArguments(bundle); - c.getSupportFragmentManager().beginTransaction() - .replace(R.id.fragmentContainerView, test, "iperf3LogFragment") - .addToBackStack("findThisFragment").commit(); - - } - }); - + li = LayoutInflater.from(context); + itemView.setBackgroundColor(context.getColor(R.color.material_dynamic_tertiary30)); + setupLinearProgressIndicator(); + setupParameterFlexBox(); + setupMetricLinearLayout(); } } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResult.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResult.java deleted file mode 100644 index 1a7f9854..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResult.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Peter Hasse - * SPDX-FileCopyrightText: 2023 Johann Hackler - * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS - * - * SPDX-License-Identifier: BSD-3-Clause-Clear - */ - -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; - -import androidx.annotation.NonNull; -import androidx.room.ColumnInfo; -import androidx.room.Entity; -import androidx.room.PrimaryKey; -import androidx.room.TypeConverters; - -import java.sql.Timestamp; - -@Entity(tableName = "iperf3_result_database") -public class Iperf3RunResult { - @NonNull - @PrimaryKey - public String uid; - - @ColumnInfo(name = "result") - public int result; - - @ColumnInfo(name = "uploaded") - public boolean uploaded; - - @ColumnInfo(name = "timestamp") - public long timestamp; - - @ColumnInfo(name = "input") - @TypeConverters({Iperf3InputConverter.class}) - public Iperf3Fragment.Iperf3Input input; - - public Iperf3RunResult(String uid, int result, boolean upload, Iperf3Fragment.Iperf3Input input, - Timestamp timestamp) { - this.uid = uid; - this.result = result; - this.uploaded = upload; - this.input = input; - this.timestamp = timestamp.getTime(); - } - - - public Iperf3RunResult() { - - } -} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResultDao.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResultDao.java deleted file mode 100644 index 158823aa..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3RunResultDao.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Peter Hasse - * SPDX-FileCopyrightText: 2023 Johann Hackler - * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS - * - * SPDX-License-Identifier: BSD-3-Clause-Clear - */ - -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; - -import androidx.lifecycle.LiveData; -import androidx.room.Dao; -import androidx.room.Delete; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; -import androidx.room.Query; -import androidx.room.Update; - -import java.util.List; - -@Dao -public interface Iperf3RunResultDao { - @Query("SELECT * FROM iperf3_result_database") - LiveData> getAll(); - - @Query("SELECT COUNT(*) FROM iperf3_result_database") - LiveData getLength(); - - @Query("SELECT uid FROM iperf3_result_database ORDER BY timestamp DESC") - List getIDs(); - - @Query("SELECT * FROM iperf3_result_database WHERE uid = :comp_uid") - Iperf3RunResult getRunResult(String comp_uid); - - @Insert - void insertAll(Iperf3RunResult... iperf3RunResults); - - @Insert(onConflict = OnConflictStrategy.REPLACE) - void insert(Iperf3RunResult iperf3RunResult); - - @Query("UPDATE iperf3_result_database SET result=:result WHERE uid=:uid") - void updateResult(String uid, int result); - - @Query("UPDATE iperf3_result_database SET uploaded=:uploaded WHERE uid=:uid") - void updateUpload(String uid, boolean uploaded); - - @Update - void update(Iperf3RunResult iperf3RunResult); - - @Delete - void delete(Iperf3RunResult iperf3RunResult); -} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Worker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Worker.java deleted file mode 100644 index 1a228178..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3Worker.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Peter Hasse - * SPDX-FileCopyrightText: 2023 Johann Hackler - * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS - * - * SPDX-License-Identifier: BSD-3-Clause-Clear - */ - -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; - -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; - -import android.app.Notification; -import android.app.PendingIntent; -import android.content.Context; -import android.graphics.Color; -import android.os.Build; -import android.util.Log; - -import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import androidx.core.app.NotificationCompat; -import androidx.room.util.StringUtil; -import androidx.work.Data; -import androidx.work.ForegroundInfo; -import androidx.work.WorkManager; -import androidx.work.Worker; -import androidx.work.WorkerParameters; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; -import java.util.Locale; - -public class Iperf3Worker extends Worker { - private static final String TAG = "iperf3Worker"; - - static { - Iperf3LibLoader.load(); - } - - private final String[] cmd; - private final String iperf3WorkerID; - private final String measurementName; - private final String timestamp; - private final int notificationID; - private final String client; - private final String protocol; - private String serverPort; - private final String ip; - private final int FOREGROUND_SERVICE_TYPE = FOREGROUND_SERVICE_TYPE_SPECIAL_USE; - - public Iperf3Worker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - cmd = getInputData().getStringArray("commands"); - measurementName = getInputData().getString("measurementName"); - iperf3WorkerID = getInputData().getString("iperf3WorkerID"); - timestamp = getInputData().getString("timestamp"); - notificationID = 100; - client = getInputData().getString("client"); - ip = getInputData().getString("ip"); - serverPort = getInputData().getString("port"); - protocol = getInputData().getString("protocol"); - - } - - private native int iperf3Wrapper(String[] argv, String cache); - - private native int iperf3Stop(); - - private ForegroundInfo createForegroundInfo(@NonNull String progress) { - - Context context = getApplicationContext(); - String id = "OMNT_notification_channel"; - PendingIntent intent = WorkManager.getInstance(context) - .createCancelPendingIntent(getId()); - Notification notification = new NotificationCompat.Builder(context, id) - .setContentTitle("iPerf3 "+ client.substring(0, 1).toUpperCase() + client.substring(1).toLowerCase()) - .setContentText(progress) - .setOngoing(true) - .setColor(Color.WHITE) - .setSmallIcon(R.mipmap.ic_launcher_foreground) - .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_DEFAULT) - .addAction(R.drawable.ic_close, "Cancel", intent) - .build(); - return new ForegroundInfo(notificationID, notification, FOREGROUND_SERVICE_TYPE); - } - - @Override - public void onStopped() { - Log.d(TAG, "onStopped: called!"); - iperf3Stop(); - } - - @NonNull - @Override - public Result doWork() { - if (serverPort == null) serverPort = "5201"; - String progress = String.format("Connected to %s:%s with %s", ip, serverPort, protocol); - if (client.equals("server")) { - progress = String.format("Running on %s:%s", ip, serverPort); - } - - setForegroundAsync(createForegroundInfo(progress)); - - int result = - iperf3Wrapper(cmd, getApplicationContext().getApplicationInfo().nativeLibraryDir); - Log.d(TAG, "doWork: " + result); - - - Data.Builder output = new Data.Builder() - .putInt("iperf3_result", result) - .putString("iperf3WorkerID", iperf3WorkerID); - if (result == 0) { - return Result.success(output.build()); - } - return Result.failure(output - .build()); - } -} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Interval.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Interval.java index 47d51b1a..fc351996 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Interval.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Interval.java @@ -2,6 +2,9 @@ import static de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.SUM_TYPE.*; +import com.google.gson.Gson; +import com.influxdb.client.JSON; + import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.STREAM_TYPE; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.SUM_TYPE; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Sum.Sum; @@ -35,7 +38,27 @@ private SUM_TYPE getSumType(JSONObject data) throws JSONException { if(data.has("jitter_ms")) return SUM_TYPE.UDP_DL; return SUM_TYPE.TCP_DL; } - + public String getNotificationString(){ + StringBuilder notificationString = new StringBuilder(); + notificationString.append("Throughput: "); + notificationString.append(Math.round(this.getSum().getBits_per_second() / 1e6)); + notificationString.append(" Mbit/s"); + notificationString.append("\n"); + notificationString.append("Direction: "); + notificationString.append(this.getSum().getSumType()); + notificationString.append("\n"); + if(sumBidirReverse != null){ + notificationString.append("--------------------\n"); + notificationString.append("Throughput: "); + notificationString.append(Math.round(this.getSumBidirReverse().getBits_per_second() / 1e6)); + notificationString.append(" Mbit/s"); + notificationString.append("\n"); + notificationString.append("Direction: "); + notificationString.append(this.getSumBidirReverse().getSumType()); + notificationString.append("\n"); + } + return notificationString.toString(); + } public Sum identifySum(JSONObject data) throws JSONException { Sum identifiedSum = null; switch (getSumType(data)){ @@ -76,4 +99,16 @@ public Sum getSum() { public Sum getSumBidirReverse() { return sumBidirReverse; } + + public String toString(){ + return new Gson().toJson(this); + } + public Interval(String line){ + this.streams = new Streams(); + Interval interval = new Gson().fromJson(line, Interval.class); + this.sum = interval.sum; + this.sumBidirReverse = interval.sumBidirReverse; + } + + } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/Streams.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/Streams.java index 4a07cf84..d1ceb420 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/Streams.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/Streams.java @@ -71,4 +71,9 @@ public void addStream(Stream stream){ public Stream getStream(int i) { return streams.get(i); } + + public void setStreams(ArrayList streams) { + this.streams.clear(); + this.streams.addAll(streams); + } } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/UDP/UDP_STREAM.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/UDP/UDP_STREAM.java index acf8e17f..869a71f7 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/UDP/UDP_STREAM.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/Interval/Streams/UDP/UDP_STREAM.java @@ -5,7 +5,6 @@ import org.json.JSONObject; public class UDP_STREAM extends Stream { - public int packets; public UDP_STREAM(){ super(); } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/start/Start.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/start/Start.java index af9ebbba..c0ed5e02 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/start/Start.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/JSON/start/Start.java @@ -1,5 +1,7 @@ package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.start; +import com.google.gson.Gson; + import java.util.ArrayList; import org.json.JSONArray; import org.json.JSONException; @@ -85,4 +87,8 @@ public TestStart getTest_start() { return test_start; } + public String toString(){ + return new Gson().toJson(this); + } + } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerFour.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerFour.java new file mode 100644 index 00000000..d96aab1d --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerFour.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3ServiceWorkerFour extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerOne.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerOne.java new file mode 100644 index 00000000..634ad263 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerOne.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3ServiceWorkerOne extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerThree.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerThree.java new file mode 100644 index 00000000..50a9636b --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerThree.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3ServiceWorkerThree extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerTwo.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerTwo.java new file mode 100644 index 00000000..94e31262 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Executor/Iperf3ServiceWorkerTwo.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Executor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3ServiceWorkerTwo extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerFour.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerFour.java new file mode 100644 index 00000000..906d3fcf --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerFour.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3MonitorServiceWorkerFour extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerOne.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerOne.java new file mode 100644 index 00000000..49d48c8e --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerOne.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3MonitorServiceWorkerOne extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerThree.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerThree.java new file mode 100644 index 00000000..6780d123 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerThree.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3MonitorServiceWorkerThree extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerTwo.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerTwo.java new file mode 100644 index 00000000..668a0b31 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Service/Monitor/Iperf3MonitorServiceWorkerTwo.java @@ -0,0 +1,7 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Service.Monitor; + +import androidx.work.multiprocess.RemoteWorkerService; + +public class Iperf3MonitorServiceWorkerTwo extends RemoteWorkerService { + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3ExecutorWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3ExecutorWorker.java new file mode 100644 index 00000000..f1af4024 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3ExecutorWorker.java @@ -0,0 +1,152 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker; + +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; +import static android.view.View.GONE; + +import static com.google.common.reflect.Reflection.getPackageName; + +import android.app.Notification; +import android.content.Context; +import android.graphics.Color; +import android.util.Log; +import android.widget.RemoteViews; + +import androidx.annotation.NonNull; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.core.app.NotificationCompat; +import androidx.work.Data; +import androidx.work.ForegroundInfo; +import androidx.work.WorkerParameters; +import androidx.work.multiprocess.RemoteListenableWorker; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.Gson; + +import java.io.File; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.PingInput; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3ResultsDataBase; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResult; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResultDao; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Iperf3LibLoader; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.Iperf3Parameter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; + +public class Iperf3ExecutorWorker extends RemoteListenableWorker { + public static final String TAG = "Iperf3ExecutorWorker"; + + static { + Iperf3LibLoader.load(); + } + private final String channelId = "OMNT_notification_channel"; + private final int FOREGROUND_SERVICE_TYPE = FOREGROUND_SERVICE_TYPE_SPECIAL_USE; + private Iperf3Input iperf3Input; + private int notificationID = 2000; + private NotificationCompat.Builder notificationBuilder; + private Notification notification; + private Iperf3ResultsDataBase db; + private Iperf3RunResultDao iperf3RunResultDao; + private Iperf3RunResult iperf3RunResult; + public Iperf3ExecutorWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + Log.d(TAG, "Iperf3ExecutorWorker: called!"); + Gson gson = new Gson(); + String iperf3InputString = getInputData().getString(Iperf3Input.INPUT); + iperf3Input = gson.fromJson(iperf3InputString, Iperf3Input.class); + int notificationNumber = getInputData().getInt(PingInput.NOTIFICATIONUMBER, 0); + notificationID += notificationNumber; + db = Iperf3ResultsDataBase.getDatabase(context); + iperf3RunResultDao = db.iperf3RunResultDao(); + + notificationBuilder = new NotificationCompat.Builder(context, channelId); + + } + + + @NonNull + @Override + public ListenableFuture startRemoteWork() { + return CallbackToFutureAdapter.getFuture(completer -> { + Log.d(TAG, "startRemoteWork: tags: "+this.getTags()); + File logFile = new File(iperf3Input.getParameter().getLogfile()); + File rawPath = new File(Iperf3Parameter.rawDirPath); + + if(!rawPath.exists()) { + rawPath.mkdirs(); + } + try { + logFile.createNewFile(); + } catch (Exception e) { + Log.d(TAG, "startRemoteWork: "+e); + return completer.set(Result.failure()); + } + Log.i(TAG, "startRemoteWork: "+TAG); + + RemoteViews notificationLayout = new RemoteViews(getApplicationContext().getPackageName(), R.layout.iperf3_notification); + notificationLayout.setTextViewText(R.id.notification_title, "Running iPerf3 test..."); + notificationLayout.setViewVisibility(R.id.notification_throughput, GONE); + notificationLayout.setViewVisibility(R.id.notification_direction, GONE); + + + setForegroundAsync(createForegroundInfo(notificationLayout)); + + int result = -1; + + Log.d(TAG, "startRemoteWork: about to call iPerf3 JNI!"); + result = iperf3Wrapper(iperf3Input.getParameter().getInputAsCommand(), getApplicationContext().getApplicationInfo().nativeLibraryDir); + Log.d(TAG, "startRemoteWork: JNI Thread: " + result); + + + + iperf3RunResultDao.updateResult(iperf3Input.getTestUUID(), result); + Data.Builder output = new Data.Builder() + .putInt("result", result) + .putString("testUUID", iperf3Input.getTestUUID()); + + + if (result == 0) { + Log.d(TAG, "startRemoteWork: iperf3Wrapper: success"); + return completer.set(Result.success(output.build())); + } + Log.d(TAG, "startRemoteWork: iperf3Wrapper: failure"); + return completer.set(Result.failure(output.build())); + }); + + + } + + + @NonNull + @Override + public ListenableFuture setProgressAsync(@NonNull Data data) { + return super.setProgressAsync(data); + } + + private native int iperf3Wrapper(String[] argv, String cache); + + + private ForegroundInfo createForegroundInfo(RemoteViews notificationLayout) { + + + notification = notificationBuilder + .setSmallIcon(R.mipmap.ic_launcher_foreground) + .setStyle(new NotificationCompat.DecoratedCustomViewStyle()) + .setCustomContentView(notificationLayout) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setColor(Color.WHITE) + .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE) + .build(); + + return new ForegroundInfo(notificationID, notification, FOREGROUND_SERVICE_TYPE); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3MonitorWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3MonitorWorker.java new file mode 100644 index 00000000..46a56243 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3MonitorWorker.java @@ -0,0 +1,308 @@ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker; + +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import android.app.Notification; +import android.content.Context; +import android.graphics.Color; +import android.os.FileObserver; +import android.util.Log; +import android.widget.RemoteViews; + +import androidx.annotation.NonNull; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.core.app.NotificationCompat; +import androidx.work.Data; +import androidx.work.ForegroundInfo; +import androidx.work.WorkQuery; +import androidx.work.WorkerParameters; +import androidx.work.multiprocess.RemoteListenableWorker; +import androidx.work.multiprocess.RemoteWorkManager; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.Gson; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3ResultsDataBase; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResult; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResultDao; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Intervals; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Error; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.start.Start; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.METRIC_TYPE; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricCalculator; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; + +public class Iperf3MonitorWorker extends RemoteListenableWorker { + public static final String TAG = "Iperf3MonitorWorker"; + + private final String channelId = "OMNT_notification_channel"; + private final int FOREGROUND_SERVICE_TYPE = FOREGROUND_SERVICE_TYPE_SPECIAL_USE; + private Iperf3Input iperf3Input; + private int notificationID = 4000; + private NotificationCompat.Builder notificationBuilder; + private Notification notification; + private Iperf3ResultsDataBase db; + private Iperf3RunResultDao iperf3RunResultDao; + private Iperf3RunResult iperf3RunResult; + private Context context; + private RemoteWorkManager remoteWorkManager; + private WorkQuery workQuery; + private ScheduledExecutorService executorService; + private BufferedReader br = null; + private String pathToFile; + private File file; + private boolean isStopped; + + private RemoteViews notificationLayout; + private FileObserver fileObserver; + + MetricCalculator metricCalculatorUL; + MetricCalculator metricCalculatorDL; + + public Iperf3MonitorWorker(@NonNull Context context, @NonNull WorkerParameters workerParams){ + super(context, workerParams); + Gson gson = new Gson(); + this.context = context; + metricCalculatorUL = new MetricCalculator(METRIC_TYPE.THROUGHPUT); + metricCalculatorDL = new MetricCalculator(METRIC_TYPE.THROUGHPUT); + String iperf3InputString = getInputData().getString(Iperf3Input.INPUT); + iperf3Input = gson.fromJson(iperf3InputString, Iperf3Input.class); + int notificationNumber = getInputData().getInt(Iperf3Input.NOTIFICATIONUMBER, 0); + notificationID += notificationNumber; + db = Iperf3ResultsDataBase.getDatabase(this.context); + iperf3RunResultDao = db.iperf3RunResultDao(); + notificationBuilder = new NotificationCompat.Builder(this.context, channelId); + iperf3RunResultDao.getRunResult(iperf3Input.getTestUUID()); + remoteWorkManager = RemoteWorkManager.getInstance(this.context); + workQuery = WorkQuery.Builder + .fromTags(Arrays.asList(iperf3Input.getTestUUID())) + .build(); + + executorService = Executors.newScheduledThreadPool(1); + + notificationLayout = new RemoteViews(getApplicationContext().getPackageName(), R.layout.iperf3_notification); + notificationLayout.setTextViewText(R.id.notification_title, "Running iPerf3 test..."); + notificationLayout.setViewVisibility(R.id.notification_throughput, GONE); + notificationLayout.setViewVisibility(R.id.notification_direction, GONE); + setForegroundAsync(createForegroundInfo(notificationLayout)); + + this.pathToFile = iperf3Input.getParameter().getLogfile(); + Log.d(TAG, "Iperf3MonitorWorker: pathToFile: "+this.pathToFile); + this.file = new File(this.pathToFile); + } + + + @NonNull + @Override + public ListenableFuture startRemoteWork() { + return CallbackToFutureAdapter.getFuture(completer -> { + + + setForegroundAsync(createForegroundInfo(notificationLayout)); + try { + br = new BufferedReader(new FileReader(file)); + br.ready(); + } catch (IOException ex) { + Log.d(TAG, "Iperf3MonitorWorker: file not found!" + ex); + } + Log.d(TAG, "doWork: starting "+this.getClass().getCanonicalName()); + Timestamp starTtime = new Timestamp(iperf3RunResultDao.getTimestampFromUid(iperf3Input.getTestUUID())); + while(true){ + Timestamp newTimestamp = new java.sql.Timestamp(System.currentTimeMillis()); + long deltaInMillis = Math.abs(newTimestamp.getTime() - starTtime.getTime()); + if(deltaInMillis > iperf3Input.getParameter().getTime()*1000){ + break; + } + String line = null; + try { + while ((line = br.readLine()) != null) { + JSONObject obj = null; + try { + obj = new JSONObject(line); + } catch (JSONException e) { + Log.d(TAG, "run: parsing line as JSONObject failed"); + break; + } + String event = null; + try { + event = obj.getString("event"); + Log.d(TAG, "run: got event: " + event); + } catch (JSONException e) { + Log.d(TAG, "run: parsing event failed"); + break; + } + switch (event) { + case "start": + Log.d(TAG, "parse: Start"); + Start start = new Start(); + JSONObject startData = null; + try { + startData = obj.getJSONObject("data"); + } catch (JSONException e) { + Log.e(TAG, "parse: getting start failed"); + Log.d(TAG, "parse: " + e); + + } + try { + start.parseStart(startData); + } catch (JSONException e) { + Log.e(TAG, "parse: parsing start failed"); + Log.d(TAG, "parse: " + e); + + } + setProgressAsync(new Data.Builder().putString("start", start.toString()).build()); + iperf3RunResultDao.updateStart(iperf3Input.getTestUUID(), start.toString()); + + + break; + case "interval": + Log.d(TAG, "parse: Interval"); + Interval interval = new Interval(); + JSONObject intervalData = null; + try { + intervalData = obj.getJSONObject("data"); + } catch (JSONException e) { + Log.e(TAG, "run: getting interval failed"); + Log.d(TAG, "parse: " + e); + + } + try { + interval.parse(intervalData); + } catch (JSONException e) { + Log.e(TAG, "run: parsing interval failed"); + Log.d(TAG, "parse: " + e); + + } + + + Log.d(TAG, "Read Thread: interval: " + interval.toString()); + if (interval.getSum().getSender()) { + metricCalculatorUL.update(interval.getSum().getBits_per_second()); + } else { + metricCalculatorDL.update(interval.getSum().getBits_per_second()); + } + + String megabitPerSecond = String.valueOf( Math.round(interval.getSum().getBits_per_second() / 1e6)); + metricCalculatorUL.update(interval.getSum().getBits_per_second()); + notificationLayout.setTextViewText(R.id.notification_title, String.format("iPerf3 %s:%s", iperf3Input.getParameter().getHost(), iperf3Input.getParameter().getPort())); + notificationLayout.setTextViewText(R.id.notification_throughput, String.format("Throughput: %s Mbit/s", megabitPerSecond)); + notificationLayout.setTextViewText(R.id.notification_direction, String.format("Direction: %s", interval.getSum().getSumType())); + notificationLayout.setViewVisibility(R.id.notification_throughput, VISIBLE); + notificationLayout.setViewVisibility(R.id.notification_direction, VISIBLE); + + + if (interval.getSumBidirReverse() != null) { + notificationLayout.setViewVisibility(R.id.notification_bidir_throughput, VISIBLE); + notificationLayout.setViewVisibility(R.id.notification_bidir_direction, VISIBLE); + notificationLayout.setTextViewText(R.id.notification_bidir_throughput, String.format("Throughput: %d Mbit/s", Math.round(interval.getSumBidirReverse().getBits_per_second() / 1e6))); + notificationLayout.setTextViewText(R.id.notification_bidir_direction, String.format("Direction: %s", interval.getSumBidirReverse().getSumType())); + metricCalculatorDL.update(interval.getSumBidirReverse().getBits_per_second()); + } + metricCalculatorDL.calcAll(); + metricCalculatorUL.calcAll(); + iperf3RunResultDao.updateMetricDL(iperf3Input.getTestUUID(), metricCalculatorDL); + iperf3RunResultDao.updateMetricUL(iperf3Input.getTestUUID(), metricCalculatorUL); + + setProgressAsync(new Data.Builder().putString("interval", interval.toString()).build()); + setForegroundAsync(createForegroundInfo(notificationLayout)); + + Intervals _intervals = iperf3RunResultDao.getIntervals(iperf3Input.getTestUUID()); + if (_intervals == null) { + _intervals = new Intervals(); + } + _intervals.addInterval(interval); + iperf3RunResultDao.updateIntervals(iperf3Input.getTestUUID(), _intervals); + break; + case "end": + Log.d(TAG, "parse: End"); + //todo + //End end = new End(); + //JSONObject endData = obj.getJSONObject("data"); + //end.parseEnd(endData); + //support.firePropertyChange("interval", null, end); + + break; + case "error": + Log.d(TAG, "parse: Error"); + de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Error error = new Error(); + String errorString = null; + iperf3RunResultDao.updateResult(iperf3Input.getTestUUID(), -1); + try { + errorString = obj.getString("data"); + } catch (JSONException e) { + Log.e(TAG, "run: getting error failed!"); + Log.d(TAG, "parse: " + e); + break; + } + try { + error.parse(errorString); + Log.d(TAG, "startRemoteWork: got error!"+error); + setProgressAsync(new Data.Builder().putString("error", error.toString()).build()); + iperf3RunResultDao.updateError(iperf3Input.getTestUUID(), error); + } catch (JSONException e) { + Log.e(TAG, "run: parsing error failed!"); + Log.d(TAG, "parse: " + e); + break; + } + + return completer.set(Result.success()); + default: + Log.d(TAG, "parse: Unknown event"); + break; + } + Thread.sleep((long) (iperf3Input.getParameter().getInterval() * 1001)); + + } + } catch (Exception e){ + Log.d(TAG, "run: error reading file: "+e); + + } + } + + + + + + + + return completer.set(Result.success()); + }); + } + + private ForegroundInfo createForegroundInfo(RemoteViews notificationLayout) { + + + notification = notificationBuilder + .setSmallIcon(R.mipmap.ic_launcher_foreground) + .setStyle(new NotificationCompat.DecoratedCustomViewStyle()) + .setCustomContentView(notificationLayout) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setColor(Color.WHITE) + .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE) + .build(); + + return new ForegroundInfo(notificationID, notification, FOREGROUND_SERVICE_TYPE); + } + + + + +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ToLineProtocolWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3ToLineProtocolWorker.java similarity index 53% rename from app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ToLineProtocolWorker.java rename to app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3ToLineProtocolWorker.java index 86cf58fc..adbeeef7 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3ToLineProtocolWorker.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3ToLineProtocolWorker.java @@ -6,184 +6,136 @@ * SPDX-License-Identifier: BSD-3-Clause-Clear */ -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; + +import android.app.Notification; import android.content.Context; +import android.graphics.Color; import android.os.Build; import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; import androidx.work.Data; +import androidx.work.ForegroundInfo; import androidx.work.Worker; import androidx.work.WorkerParameters; -import com.google.common.base.Splitter; +import com.google.gson.Gson; import com.influxdb.client.domain.WritePrecision; import com.influxdb.client.write.Point; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; +import java.util.ArrayList; import java.util.LinkedList; -import java.util.Map; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.DataProvider.DeviceInformation; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.GlobalVars; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.InfluxdbConnection; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.InfluxdbConnections; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3ResultsDataBase; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResult; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResultDao; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Interval; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.Stream; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.TCP.TCP_UL_STREAM; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.JSON.Interval.Streams.UDP.UDP_DL_STREAM; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; public class Iperf3ToLineProtocolWorker extends Worker { - private static final String TAG = "Iperf3UploadWorker"; + public static final String TAG = "Iperf3ToLineProtocolWorker"; InfluxdbConnection influx; - private String rawIperf3file; - private String measurementName; - private String ip; + private final int FOREGROUND_SERVICE_TYPE = FOREGROUND_SERVICE_TYPE_SPECIAL_USE; private SharedPreferencesGrouper spg; - - private String port; - private String bandwidth; - private String duration; - private String intervalIperf; - private String bytes; - private final String protocol; - private final String iperf3LineProtocolFile; - + private Notification notification; + private NotificationCompat.Builder notificationBuilder; + private Iperf3RunResultDao iperf3RunResultDao; private final DeviceInformation di = GlobalVars.getInstance().get_dp().getDeviceInformation(); - - private final boolean rev; - private final boolean biDir; - private final boolean oneOff; - private final boolean client; - - private final String runID; + private int notificationID; + private Iperf3Input iperf3Input; public Iperf3ToLineProtocolWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); - rawIperf3file = getInputData().getString("rawIperf3file"); - - ip = getInputData().getString("ip"); - measurementName = getInputData().getString("measurementName"); - iperf3LineProtocolFile = getInputData().getString("iperf3LineProtocolFile"); - port = getInputData().getString("port"); - if(port == null) - port = "5201"; - protocol = getInputData().getString("protocol"); - bandwidth = getInputData().getString("bandwidth"); - - if(bandwidth == null){ - if(protocol.equals("TCP")) { - bandwidth = "unlimited"; - } else { - bandwidth = "1000"; - } - } - - duration = getInputData().getString("duration"); - if(duration == null) - duration = "10"; - intervalIperf = getInputData().getString("interval"); - if(intervalIperf == null) - intervalIperf = "1"; - bytes = getInputData().getString("bytes"); - if(bytes == null){ - if(protocol.equals("TCP")) { - bytes = "8"; - } else { - bytes = "1470"; - } - } - - rev = getInputData().getBoolean("rev", false); - biDir = getInputData().getBoolean("biDir",false); - oneOff = getInputData().getBoolean("oneOff",false); - client = getInputData().getBoolean("client",false); - runID = getInputData().getString("iperf3WorkerID"); + Gson gson = new Gson(); + String iperf3InputString = getInputData().getString(Iperf3Input.INPUT); + iperf3Input = gson.fromJson(iperf3InputString, Iperf3Input.class); + notificationBuilder = new NotificationCompat.Builder(getApplicationContext(), "OMNT_notification_channel"); + notificationID = 200+getInputData().getInt(Iperf3Input.NOTIFICATIONUMBER, 0); spg = SharedPreferencesGrouper.getInstance(getApplicationContext()); + setForegroundAsync(createForegroundInfo("Processing iPerf3 data")); + iperf3RunResultDao = Iperf3ResultsDataBase.getDatabase(getApplicationContext()).iperf3RunResultDao(); } - - private void setup(){ - influx = InfluxdbConnections.getRicInstance(getApplicationContext()); - } - - - public Map getTagsMap() { - String tags = spg.getSharedPreference(SPType.logging_sp).getString("tags", "").strip().replace(" ", ""); - Map tags_map = Collections.emptyMap(); - if (!tags.isEmpty()) { - try { - tags_map = Splitter.on(',').withKeyValueSeparator('=').split(tags); - } catch (IllegalArgumentException e) { - Log.d(TAG, "can't parse tags, ignoring"); - } - } - Map tags_map_modifiable = new HashMap<>(tags_map); - tags_map_modifiable.put("measurement_name", spg.getSharedPreference(SPType.logging_sp).getString("measurement_name", "OMNT")); - tags_map_modifiable.put("manufacturer", di.getManufacturer()); - tags_map_modifiable.put("model", di.getModel()); - tags_map_modifiable.put("sdk_version", String.valueOf(di.getAndroidSDK())); - tags_map_modifiable.put("android_version", di.getAndroidRelease()); - tags_map_modifiable.put("secruity_patch", di.getSecurityPatchLevel()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - tags_map_modifiable.put("soc_model", di.getSOCModel()); - } - tags_map_modifiable.put("radio_version", Build.getRadioVersion()); - return tags_map_modifiable; + private ForegroundInfo createForegroundInfo(String progress) { + notification = notificationBuilder + .setContentTitle("iPerf32LineProtocol") + .setContentText(progress) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setColor(Color.WHITE) + .setSmallIcon(R.mipmap.ic_launcher_foreground) + .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_DEFAULT) + .build(); + return new ForegroundInfo(notificationID, notification, FOREGROUND_SERVICE_TYPE); } @NonNull @Override public Result doWork() { - setup(); Data output = new Data.Builder().putBoolean("iperf3_upload", false).build(); - Iperf3Parser iperf3Parser = new Iperf3Parser(rawIperf3file); - iperf3Parser.parse(); + Iperf3RunResult iperf3RunResult = iperf3RunResultDao.getRunResult(iperf3Input.getTestUUID()); - - long timestamp = Integer.toUnsignedLong( iperf3Parser.getStart().getTimestamp().getTimesecs())*1000; + long timestamp = Integer.toUnsignedLong( iperf3RunResult.start.getTimestamp().getTimesecs())*1000; Log.d(TAG, "doWork: "+timestamp); String role = "server"; - if(iperf3Parser.getStart().getConnecting_to() != null){ + if(iperf3RunResult.start.getConnecting_to() != null){ role = "client"; } LinkedList points = new LinkedList(); - for (Interval interval: iperf3Parser.getIntervals().getIntervalArrayList()) { + ArrayList intervals = iperf3RunResult.intervals.getIntervalArrayList(); + for (Interval interval: intervals) { long tmpTimestamp = timestamp + (long) (interval.getSum().getEnd() * 1000); - int intervalIdx = iperf3Parser.getIntervals().getIntervalArrayList().indexOf(interval); + int intervalIdx = intervals.indexOf(interval); for (Stream stream: interval.getStreams().getStreamArrayList()){ Point point = new Point("Iperf3"); - point.addTag("run_uid", runID); - point.addTag("bidir", String.valueOf(biDir)); + + point.addTag(Iperf3Input.TESTUUID, iperf3Input.getTestUUID()); + point.addTag(Iperf3Input.SEQUENCEUUID, iperf3Input.getSequenceUUID()); + point.addTag(Iperf3Input.MEASUREMENTUUID, iperf3Input.getMeasurementUUID()); + point.addTag(Iperf3Input.CAMPAIGNUUID, iperf3Input.getCampaignUUID()); + point.addTag(Iperf3Input.IPERF3UUID, iperf3Input.getParameter().getiPerf3UUID()); + + + + + point.addTag("bidir", String.valueOf(iperf3Input.getParameter().getBidir())); point.addTag("sender", String.valueOf(stream.getSender())); point.addTag("role", role); point.addTag("socket", String.valueOf(stream.getSocket())); - point.addTag("protocol", protocol); - point.addTag("interval", intervalIperf); - point.addTag("version", iperf3Parser.getStart().getVersion()); - point.addTag("reversed", String.valueOf(rev)); - point.addTag("oneOff", String.valueOf(oneOff)); - point.addTag("connectingToHost", iperf3Parser - .getStart() + point.addTag("protocol", iperf3RunResult.start.getTest_start().protocol); + point.addTag("interval", String.valueOf(iperf3Input.getParameter().getInterval())); + point.addTag("version", iperf3RunResult.start.getVersion()); + point.addTag("reversed", String.valueOf(iperf3Input.getParameter().getReverse())); + point.addTag("oneOff", String.valueOf(iperf3Input.getParameter().getOneOff())); + point.addTag("connectingToHost", iperf3RunResult + .start .getConnecting_to() .getHost()); - point.addTag("connectingToPort", String.valueOf(iperf3Parser - .getStart() + point.addTag("connectingToPort", String.valueOf(iperf3RunResult + .start .getConnecting_to() .getPort())); - point.addTag("bandwidth", bandwidth); - point.addTag("duration", duration); - point.addTag("bytesToTransmit", bytes); + point.addTag("bandwidth", iperf3Input.getParameter().getBitrate()); + point.addTag("duration", String.valueOf(iperf3Input.getParameter().getTime())); + point.addTag("bytesToTransmit", String.valueOf(iperf3Input.getParameter().getBytes())); point.addTag("streams", String.valueOf(interval.getStreams().size())); point.addTag("streamIdx", String.valueOf(interval.getStreams().getStreamArrayList().indexOf(stream))); point.addTag("intervalIdx", String.valueOf(intervalIdx)); @@ -221,13 +173,14 @@ public Result doWork() { point.time(tmpTimestamp, WritePrecision.MS); points.add(point); + setForegroundAsync(createForegroundInfo("Processing iPerf3 data: "+intervalIdx+"/"+intervals.size())); } } // is needed when only --udp is, otherwise no lostpackets/lostpercent parsed if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { for (Point point:points) { - point.addTags(getTagsMap()); + point.addTags(GlobalVars.getInstance().get_dp().getTagsMap()); } } @@ -235,7 +188,7 @@ public Result doWork() { FileOutputStream iperf3Stream = null; try { - iperf3Stream = new FileOutputStream(iperf3LineProtocolFile, true); + iperf3Stream = new FileOutputStream(iperf3Input.getParameter().getLineProtocolFile(), true); } catch (FileNotFoundException e) { Toast.makeText(getApplicationContext(), "logfile not created", Toast.LENGTH_SHORT).show(); Log.d(TAG,e.toString()); diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3UploadWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3UploadWorker.java similarity index 78% rename from app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3UploadWorker.java rename to app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3UploadWorker.java index b3ff8d43..54e9cbcf 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Iperf3UploadWorker.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Iperf3/Worker/Iperf3UploadWorker.java @@ -6,7 +6,7 @@ * SPDX-License-Identifier: BSD-3-Clause-Clear */ -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3; +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Worker; import android.content.Context; import android.util.Log; @@ -16,6 +16,8 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; +import com.google.gson.Gson; + import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; @@ -25,16 +27,18 @@ import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.InfluxdbConnection; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.InfluxdbConnections; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; public class Iperf3UploadWorker extends Worker { - private static final String TAG = "Iperf3UploadWorker"; + public static final String TAG = "Iperf3UploadWorker"; InfluxdbConnection influx; - private final String iperf3LineProtocolFile; - + private Iperf3Input iperf3Input; public Iperf3UploadWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); - iperf3LineProtocolFile = getInputData().getString("iperf3LineProtocolFile"); + Gson gson = new Gson(); + String iperf3InputString = getInputData().getString(Iperf3Input.INPUT); + iperf3Input = gson.fromJson(iperf3InputString, Iperf3Input.class); } private void setup(){ influx = InfluxdbConnections.getRicInstance(getApplicationContext()); @@ -61,17 +65,17 @@ public Result doWork() { } BufferedReader br; try { - br = new BufferedReader(new FileReader(iperf3LineProtocolFile)); + br = new BufferedReader(new FileReader(iperf3Input.getParameter().getLineProtocolFile())); } catch (FileNotFoundException | NullPointerException e) { Log.d(TAG,e.toString()); return Result.failure(output); } List points = br.lines().collect(Collectors.toList()); try { - Log.d(TAG, String.format("doWork: uploading %s", iperf3LineProtocolFile)); + Log.d(TAG, String.format("doWork: uploading %s", iperf3Input.getParameter().getLineProtocolFile())); influx.writeRecords(points); } catch (IOException e) { - Log.d(TAG, String.format("doWork: upload of %s failed!", iperf3LineProtocolFile)); + Log.d(TAG, String.format("doWork: upload of %s failed!", iperf3Input.getParameter().getLineProtocolFile())); return Result.failure(output); } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/LoggingService.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/LoggingService.java index 447bd96f..c3e0b2f7 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/LoggingService.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/LoggingService.java @@ -370,7 +370,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { } if (spg.getSharedPreference(SPType.logging_sp).getBoolean("enable_local_influx_log", false)) { - setupLocalFile(); + //TODO } setupNotification(); @@ -609,7 +609,9 @@ private void stopLocalInfluxDB() { public void run() { if (ic == null) return; boolean newInfluxConnectionStatus = ic.ping(); - Log.d(TAG, "run: monitorInfluxDBConnectionStatus: "+newInfluxConnectionStatus); + if(!newInfluxConnectionStatus) { + Log.d(TAG, "InfluxDB not reachable"); + } if(newInfluxConnectionStatus != influxConnectionStatus) { influxConnectionStatus = newInfluxConnectionStatus; updateNotification(); diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Handler.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Handler.java new file mode 100644 index 00000000..74db5d15 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Handler.java @@ -0,0 +1,41 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.MQTT.Handler; + +import android.content.Context; +import android.util.Log; + +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkInfo; +import androidx.work.WorkManager; +import androidx.work.multiprocess.RemoteWorkContinuation; +import androidx.work.multiprocess.RemoteWorkManager; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.PingInput; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.Iperf3Parameter; + +abstract public class Handler { + private final String TAG = "Handler"; + abstract public void parsePayload(String payload) throws JSONException; + + public Handler() { + } + abstract public ArrayList getExecutorWorkRequests(Context context); + + abstract public ArrayList getMonitorWorkRequests(Context context); + + abstract public ArrayList getToLineProtocolWorkRequests(Context context); + + abstract public ArrayList getUploadWorkRequests(Context context); + + abstract public void preperareSequence(Context context); + + abstract public void enableSequence(); + + abstract public void disableSequence(Context context); +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Iperf3Handler.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Iperf3Handler.java new file mode 100644 index 00000000..befa824e --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/Iperf3Handler.java @@ -0,0 +1,174 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.MQTT.Handler; + +import android.content.Context; +import android.util.Log; + +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkInfo; +import androidx.work.WorkManager; +import androidx.work.WorkRequest; +import androidx.work.multiprocess.RemoteWorkContinuation; +import androidx.work.multiprocess.RemoteWorkManager; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + + +import java.io.File; +import java.sql.Timestamp; +import java.util.ArrayList; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.Iperf3Input; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.PingInput; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3ResultsDataBase; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResult; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Iperf3.Database.RunResult.Iperf3RunResultDao; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.Iperf3Parameter; + +public class Iperf3Handler extends Handler { + private final String TAG = "Iperf3Handler"; + private ArrayList iperf3Inputs; + private boolean isEnable = false; + private ArrayList continuations; + private Iperf3RunResultDao iperf3RunResultDao; + @Override + public void parsePayload(String payload) throws JSONException { + iperf3Inputs = new ArrayList<>(); + JSONArray tests = new JSONArray(payload); + for (int i = 0; i < tests.length(); i++) { + JSONObject test = tests.getJSONObject(i); + String testUUID = test.getString("testUUID"); + String measurementUUUID = test.getString("measurementUUID"); + String sequenceUUID = test.getString("sequenceUUID"); + String campaignUUID = test.getString("campaignUUID"); + String device = test.getString("device"); + String testType = test.getString("type"); + if(!testType.equals("iperf3")) continue; + JSONObject params = test.getJSONObject("params"); + + Iperf3Parameter iperf3Parameter = new Iperf3Parameter(params, testUUID); + if(iperf3Parameter == null) continue; + Iperf3Input iperf3Input = new Iperf3Input(iperf3Parameter, testUUID, sequenceUUID, measurementUUUID,campaignUUID); + iperf3Input.setTimestamp(new Timestamp(System.currentTimeMillis())); + iperf3Inputs.add(iperf3Input); + + Iperf3RunResult iperf3RunResult = new Iperf3RunResult(iperf3Input.getTestUUID(), -100, false, iperf3Input, new java.sql.Timestamp(System.currentTimeMillis())); + iperf3RunResultDao.insert(iperf3RunResult); + File logFile = new File(iperf3Input.getParameter().getLogfile()); + if(logFile.exists()) { + logFile.delete(); + } + + } + } + + public ArrayList getTestUUIDs() { + ArrayList testUUIDs = new ArrayList<>(); + for(Iperf3Input iperf3Input : iperf3Inputs) { + testUUIDs.add(iperf3Input.getTestUUID()); + } + return testUUIDs; + } + public Iperf3Handler(Context context) { + super(); + iperf3RunResultDao = Iperf3ResultsDataBase.getDatabase(context).iperf3RunResultDao(); + } + @Override + public ArrayList getExecutorWorkRequests(Context context) { + ArrayList executorWorkRequests = new ArrayList<>(); + for(Iperf3Input iperf3Input : iperf3Inputs) { + executorWorkRequests.add(iperf3Input.getWorkRequestExecutor(iperf3Inputs.indexOf(iperf3Input), context.getPackageName())); + } + return executorWorkRequests; + } + @Override + public ArrayList getMonitorWorkRequests(Context context) { + ArrayList monitorWorkRequests = new ArrayList<>(); + for(Iperf3Input iperf3Input : iperf3Inputs) { + monitorWorkRequests.add(iperf3Input.getWorkRequestMonitor(iperf3Inputs.indexOf(iperf3Input), context.getPackageName())); + } + return monitorWorkRequests; + } + + + + + @Override + public ArrayList getToLineProtocolWorkRequests(Context context) { + ArrayList lineProtocolWorkRequests = new ArrayList<>(); + for(Iperf3Input iperf3Input : iperf3Inputs) { + lineProtocolWorkRequests.add(iperf3Input.getWorkRequestLineProtocol(iperf3Inputs.indexOf(iperf3Input), context.getPackageName())); + } + return lineProtocolWorkRequests; + } + @Override + public ArrayList getUploadWorkRequests(Context context) { + ArrayList uploadWorkRequests = new ArrayList<>(); + for(Iperf3Input iperf3Input : iperf3Inputs) { + uploadWorkRequests.add(iperf3Input.getWorkRequestUpload(iperf3Inputs.indexOf(iperf3Input), context.getPackageName())); + } + return uploadWorkRequests; + } + + @Override + public void preperareSequence(Context context){ + continuations = new ArrayList<>(); + Log.d(TAG, "enableSequence: called!"); + if(iperf3Inputs.isEmpty()) { + Log.e(TAG, "No iperf3 tests to run"); + return; + }; + + ArrayList> workRequestss = new ArrayList<>(); + RemoteWorkManager remoteWorkManager = RemoteWorkManager.getInstance(context); + for(Iperf3Input iperf3Input: iperf3Inputs){ + remoteWorkManager.cancelAllWorkByTag(iperf3Input.getTestUUID()); + ArrayList workRequests = new ArrayList<>(); + int i = iperf3Inputs.indexOf(iperf3Input); + workRequests.add(iperf3Input.getWorkRequestExecutor(i, context.getPackageName())); + workRequests.add(iperf3Input.getWorkRequestMonitor(i, context.getPackageName())); + workRequests.add(iperf3Input.getWorkRequestLineProtocol(i, context.getPackageName())); + workRequests.add(iperf3Input.getWorkRequestUpload(i, context.getPackageName())); + workRequestss.add(workRequests); + Log.d(TAG, "enableSequence: workRequests size: "+workRequests.size()); + } + + + Log.d(TAG, "enableSequence: workRequestss size: "+workRequestss.size()); + for(ArrayList workRequests: workRequestss){ + RemoteWorkContinuation remoteWorkContinuation = remoteWorkManager.beginWith(workRequests.subList(0, 2)).then(workRequests.get(2)).then(workRequests.get(3)); + continuations.add(remoteWorkContinuation); + } + Log.d(TAG, "enableSequence: continuations size: "+continuations.size()); + } + + @Override + public void enableSequence(){ + + for(RemoteWorkContinuation continuation: continuations){ + continuation.enqueue(); + } + } + + @Override + public void disableSequence(Context context){ + RemoteWorkManager workManager = RemoteWorkManager.getInstance(context); + for(Iperf3Input iperf3Input: iperf3Inputs){ + workManager.cancelAllWorkByTag(iperf3Input.getTestUUID()); + } + } + + public boolean isEnable() { + return isEnable; + } + + public void setEnable(boolean enable) { + isEnable = enable; + } +} + + + + + diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/PingHandler.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/PingHandler.java new file mode 100644 index 00000000..8ecb8438 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/Handler/PingHandler.java @@ -0,0 +1,116 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.MQTT.Handler; + +import android.content.Context; + +import androidx.work.OneTimeWorkRequest; +import androidx.work.multiprocess.RemoteWorkContinuation; +import androidx.work.multiprocess.RemoteWorkManager; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.ArrayList; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.PingInput; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.PingParameter; + +public class PingHandler extends Handler { + + public static final String TAG = "PingHandler"; + private ArrayList pingInputs = new ArrayList<>(); + private boolean isEnable = false; + private RemoteWorkContinuation workContinuation; + @Override + public void parsePayload(String payload) throws JSONException { + pingInputs.clear(); + JSONArray tests = new JSONArray(payload); + for (int i = 0; i < tests.length(); i++) { + JSONObject test = tests.getJSONObject(i); + String testUUID = test.getString("testUUID"); + String measurementUUUID = test.getString("measurementUUID"); + String sequenceUUID = test.getString("sequenceUUID"); + String campaignUUID = test.getString("campaignUUID"); + String device = test.getString("device"); + String testType = test.getString("type"); + if(!testType.equals("ping")) continue; + JSONObject params = test.getJSONObject("params"); + + PingParameter pingParameter = new PingParameter(params, testUUID); + if(pingParameter == null) continue; + PingInput pingInput = new PingInput(pingParameter, testUUID, sequenceUUID, measurementUUUID,campaignUUID); + File logFile = new File(pingInput.getParameter().getLogfile()); + if(logFile.exists()) { + logFile.delete(); + } + pingInputs.add(pingInput); + + } + + } + + public ArrayList getTestUUIDs() { + ArrayList testUUIDs = new ArrayList<>(); + for(PingInput pingInput : pingInputs) { + testUUIDs.add(pingInput.getTestUUID()); + } + return testUUIDs; + } + + @Override + public ArrayList getExecutorWorkRequests(Context context) { + ArrayList executorWorkRequests = new ArrayList<>(); + for(PingInput pingInput : pingInputs) { + executorWorkRequests.add(pingInput.getWorkRequestExecutor(pingInputs.indexOf(pingInput), context.getPackageName())); + } + return executorWorkRequests; + } + + @Override + public ArrayList getMonitorWorkRequests(Context context) { + return null; + } + + @Override + public ArrayList getToLineProtocolWorkRequests(Context context) { + ArrayList toLineProtocolWorkRequests = new ArrayList<>(); + for(PingInput pingInput : pingInputs) { + toLineProtocolWorkRequests.add(pingInput.getWorkRequestLineProtocol(pingInputs.indexOf(pingInput), context.getPackageName())); + } + return toLineProtocolWorkRequests; + } + @Override + public ArrayList getUploadWorkRequests(Context context) { + ArrayList uploadWorkRequests = new ArrayList<>(); + for(PingInput pingInput : pingInputs) { + uploadWorkRequests.add(pingInput.getWorkRequestUpload(pingInputs.indexOf(pingInput), context.getPackageName())); + } + return uploadWorkRequests; + } + @Override + public void preperareSequence(Context context) { + workContinuation = RemoteWorkManager.getInstance(context).beginWith(getExecutorWorkRequests(context)).then(getToLineProtocolWorkRequests(context)).then(getUploadWorkRequests(context)); + } + @Override + public void enableSequence() { + if(workContinuation == null){ + return; + } + workContinuation.enqueue(); + } + + @Override + public void disableSequence(Context context) { + + + } + + public boolean isEnable() { + return isEnable; + } + + public void setEnable(boolean enable) { + isEnable = enable; + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/MQTTService.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/MQTTService.java new file mode 100644 index 00000000..708e4f73 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MQTT/MQTTService.java @@ -0,0 +1,506 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.MQTT; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.work.Data; +import androidx.work.ExistingWorkPolicy; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkInfo; +import androidx.work.multiprocess.RemoteWorkManager; + + +import com.hivemq.client.mqtt.datatypes.MqttQos; +import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import com.hivemq.client.mqtt.mqtt5.message.connect.connack.Mqtt5ConnAck; +import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PayloadFormatIndicator; +import com.influxdb.client.domain.Run; + +import org.json.JSONObject; + +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.CustomEventListener; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.MQTT.Handler.Iperf3Handler; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.MQTT.Handler.PingHandler; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.MainActivity; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.RemoteWorkInfoChecker; + +public class MQTTService extends Service { + private static final String TAG = "MQTTService"; + private Context context; + private SharedPreferences mqttSP; + private NotificationCompat.Builder builder; + public NotificationManager nm; + private Handler notificationHandler; + private Mqtt5AsyncClient client; + private SharedPreferencesGrouper spg; + private String deviceName; + private Iperf3Handler iperf3Handler; + private PingHandler pingHandler; + private boolean isEnabled = false; + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void setupSharedPreferences(){ + spg = SharedPreferencesGrouper.getInstance(context); + mqttSP = spg.getSharedPreference(SPType.mqtt_sp); + mqttSP.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> { + if(key == null) return; + if (key.equals("mqtt_host")) { + Log.d(TAG, "MQTT Host update: " + sharedPreferences.getString("mqtt_host", "")); + client.disconnect(); + createClient(); + createNotification(); + } + }); + } + + public void createClient(){ + String addressString = mqttSP.getString("mqtt_host", "localhost:1883"); + String host = null; + int port = -1; + try { + host = addressString.split(":")[0]; + port = Integer.parseInt(addressString.split(":")[1]); + } catch (Exception e) { + Log.e(TAG, "createClient: Invalid address string: " + addressString); + return; + } + if(host == null || port == -1){ + Log.e(TAG, "createClient: Invalid address string: " + addressString); + return; + } + InetSocketAddress address = new InetSocketAddress(host, port); + client = Mqtt5Client.builder() + .identifier(deviceName) + .serverAddress(address) + .automaticReconnect() + .initialDelay(200, TimeUnit.MILLISECONDS) + .maxDelay(30, TimeUnit.SECONDS) + .applyAutomaticReconnect() + .addConnectedListener(context -> { + Log.i(TAG, "createClient: Connected to MQTT server"); + createNotification(); + publishToTopic(String.format("device/%s/status", deviceName), "1", false); + }) + .addDisconnectedListener(context -> { + Log.i(TAG, "createClient: Disconnected from MQTT server"); + createNotification(); + }) + .willPublish() + .topic(String.format("device/%s/status", deviceName)) + .qos(MqttQos.EXACTLY_ONCE) + .payload("0".getBytes()) + .retain(true) + .payloadFormatIndicator(Mqtt5PayloadFormatIndicator.UTF_8) + .contentType("text/plain") + .noMessageExpiry() + .applyWillPublish() + .buildAsync(); + + Log.i(TAG, "createClient: Client created with address: " + addressString); + } + + private void createNotification(){ + StringBuilder s = new StringBuilder(); + String address = spg.getSharedPreference(SPType.mqtt_sp).getString("mqtt_host", "None"); + if(address.equals("None")){ + s.append("MQTT Host: None\n"); + } else { + s.append("Host: ").append(address).append("\n"); + s.append("State: ").append(client.getState().toString()).append("\n"); + } + builder.setStyle(new NotificationCompat.BigTextStyle() + .bigText(s)); + nm.notify(3, builder.build()); + } + + @Override + public void onCreate() { + super.onCreate(); + nm = getSystemService(NotificationManager.class); + Intent notificationIntent = new Intent(this, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // create notification + builder = new NotificationCompat.Builder(this, "OMNT_notification_channel") + .setContentTitle(getText(R.string.mqtt_service_running)) + .setSmallIcon(R.mipmap.ic_launcher_foreground) + .setColor(Color.WHITE) + .setContentIntent(pendingIntent) + // prevent to swipe the notification away + .setOngoing(true) + // don't wait 10 seconds to show the notification + .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE); + } else { + // create notification + builder = new NotificationCompat.Builder(this, "OMNT_notification_channel") + .setContentTitle(getText(R.string.mqtt_service_running)) + .setSmallIcon(R.mipmap.ic_launcher_foreground) + .setColor(Color.WHITE) + .setContentIntent(pendingIntent) + // prevent to swipe the notification away + .setOngoing(true); + } + + } + + public void publishToTopic(String topic, String message, boolean retain){ + client.publishWith() + .topic(topic) + .qos(MqttQos.EXACTLY_ONCE) + .payload(message.getBytes()) + .retain(retain) + .send(); + } + + public void disconnectClient(){ + CompletableFuture disconnect = client.disconnect(); + disconnect.whenComplete((aVoid, throwable) -> { + if(throwable != null){ + Log.e(TAG, "disconnectClient: Error disconnecting from MQTT server: " + throwable.getMessage()); + } else { + Log.i(TAG, "disconnectClient: Disconnected from MQTT server"); + } + }); + } + + public void connectClient(){ + + CompletableFuture connAck = client.connectWith() + .keepAlive(1) + .willPublish() + .topic(String.format("device/%s/status", deviceName)) + .qos(MqttQos.EXACTLY_ONCE) + .payload("0".getBytes()) + .retain(true) + .payloadFormatIndicator(Mqtt5PayloadFormatIndicator.UTF_8) + .contentType("text/plain") + .noMessageExpiry() + .applyWillPublish() + .send(); + + connAck.whenComplete((mqtt5ConnAck, throwable) -> { + if(throwable != null){ + Log.e(TAG, "connectClient: Error connecting to MQTT server: " + throwable.getMessage()); + } else { + Log.i(TAG, "connectClient: Connected to MQTT server"); + publishToTopic(String.format("device/%s/status", deviceName), "1", true); + } + }); + } + + private boolean parseBoolean(String value){ + switch (value.toLowerCase()){ + case "true": + case "1": + return true; + case "false": + case "0": + return false; + } + return false; + } + + private void handleConfigMessage(String topic, String payload){ + + + // config logging service + if(topic.contains("/logging/enable")){ + Log.d(TAG, "handleConfigMessage: Enable Logging: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("enable_logging", parseBoolean(payload)).apply(); + return; + } + + if(topic.contains("/logging/start_on_boot")){ + Log.d(TAG, "handleConfigMessage: Start on Boot: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("start_logging_on_boot", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/logging/notification_update_enabled")){ + Log.d(TAG, "handleConfigMessage: Notification Update: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("enable_notification_update", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/logging/interval_ms")){ + Log.d(TAG, "handleConfigMessage: Logging Interval: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putString("logging_interval", payload).apply(); + return; + } + + // config influxdv_v2 parameter + if(topic.contains("/influxv2/enabled")){ + Log.d(TAG, "handleConfigMessage: Enable Influx: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("enable_influx", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/influxv2/address")){ + Log.d(TAG, "handleConfigMessage: Influx Address: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putString("influx_URL", payload).apply(); + return; + } + if(topic.contains("/influxv2/token")){ + Log.d(TAG, "handleConfigMessage: Influx Token received!"); + spg.getSharedPreference(SPType.logging_sp).edit().putString("influx_token", payload).apply(); + return; + } + if(topic.contains("/influxv2/bucket")){ + Log.d(TAG, "handleConfigMessage: Influx Bucket: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putString("influx_bucket", payload).apply(); + return; + } + if(topic.contains("/influxv2/org")){ + Log.d(TAG, "handleConfigMessage: Influx Org: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putString("influx_org", payload).apply(); + return; + } + if(topic.contains("/influxv2/tags")){ + Log.d(TAG, "handleConfigMessage: Influx Tags: " + payload); + //spg.getSharedPreference(SPType.logging_sp).edit().putString("influx_org", payload).apply(); + //TODO + return; + } + + + // config log file + if(topic.contains("/file/enabled")){ + Log.d(TAG, "handleConfigMessage: Enable Local File Log: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("enable_local_file_log", parseBoolean(payload)).apply(); + return; + } + + // config logging content + if(topic.contains("/content/measurement_name")){ + Log.d(TAG, "handleConfigMessage: Measurement Name: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putString("measurement_name", payload).apply(); + return; + } + if(topic.contains("/content/network_information")){ + Log.d(TAG, "handleConfigMessage: Network Information: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("influx_network_data", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/content/signal_information")){ + Log.d(TAG, "handleConfigMessage: Signal Information: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("log_signal_data", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/content/cell_information")){ + Log.d(TAG, "handleConfigMessage: Cell Information: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("influx_cell_data", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/content/neighbour_cells")){ + Log.d(TAG, "handleConfigMessage: Neighbour Cells: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("log_neighbour_cells", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/content/throughput_information")){ + Log.d(TAG, "handleConfigMessage: Throughput Information: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("influx_throughput_data", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/content/wifi_information")){ + Log.d(TAG, "handleConfigMessage: Wifi Information: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("log_wifi_data", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/content/battery_information")){ + Log.d(TAG, "handleConfigMessage: Battery Information: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("influx_battery_data", parseBoolean(payload)).apply(); + return; + } + if(topic.contains("/content/ip_information")){ + Log.d(TAG, "handleConfigMessage: IP Information: " + payload); + spg.getSharedPreference(SPType.logging_sp).edit().putBoolean("influx_ip_address_data", parseBoolean(payload)).apply(); + return; + } + + if(topic.contains("/iperf3/command")){ + Log.d(TAG, "handleConfigMessage: Iperf3 Command: " + payload); + iperf3Handler = new Iperf3Handler(context); + try { + iperf3Handler.parsePayload(payload); + } catch (Exception e) { + Log.e(TAG, "handleConfigMessage: Error parsing iperf3 payload: " + e.getMessage()); + //TODO PUBLISH ERROR + } + return; + } + if(topic.contains("/iperf3/enable")){ + Log.d(TAG, "handleConfigMessage: Enable Iperf3: " + payload); + + if(iperf3Handler != null && parseBoolean(payload)){ + iperf3Handler.preperareSequence(getApplicationContext()); + //TODO PUBLISH iperf3 sequence enabled + } else if(iperf3Handler != null && !parseBoolean(payload)){ + iperf3Handler.disableSequence(getApplicationContext()); + //TODO PUBLISH iperf3 sequence disabled + } + return; + } + + if(topic.contains("/ping/command")){ + Log.d(TAG, "handleConfigMessage: Ping Command: " + payload); + pingHandler = new PingHandler(); + try { + pingHandler.parsePayload(payload); + } catch (Exception e) { + Log.e(TAG, "handleConfigMessage: Error parsing ping payload: " + e.getMessage()); + //TODO PUBLISH ERROR + } + return; + } + + if(topic.contains("/ping/enable")){ + Log.d(TAG, "handleConfigMessage: Enable Ping: " + payload); + + if(pingHandler != null && parseBoolean(payload)){ + pingHandler.preperareSequence(getApplicationContext()); + //TODO PUBLISH ping sequence enabled + } else if(pingHandler != null && !parseBoolean(payload)){ + pingHandler.disableSequence(getApplicationContext()); + //TODO PUBLISH ping sequence disabled + } + return; + } + + if(topic.contains("/sequence/enable")){ + Log.d(TAG, "handleConfigMessage: Enable Sequence: " + payload); + setEnabled(parseBoolean(payload)); + return; + } + + Log.d(TAG, "handleConfigMessage: No matching topic found: " + topic); + + return; + } + + private void subsribetoTopic(String topic){ + client.subscribeWith() + .topicFilter(topic) + .qos(MqttQos.AT_LEAST_ONCE) + .callback(publish -> { + if (!publish.getPayload().isPresent()){ + Log.e(TAG, "Received empty payload from topic: " + publish.getTopic()); + return; + }; + Log.d(TAG, "Received message: " + publish.getTopic()); + String payload = StandardCharsets.UTF_8.decode(publish.getPayload().get()).toString(); + handleConfigMessage(publish.getTopic().toString(), payload); + }) + .send() + .whenComplete((subAck, throwable) -> { + if(throwable != null){ + Log.e(TAG, "Error subscribing to topic: " + topic + " Error: " + throwable.getMessage()); + } else { + Log.i(TAG, "Subscribed to topic: " + topic); + } + }); + } + + + private void subscribeToAllTopics(){ + subsribetoTopic(String.format("device/%s/#", deviceName)); + } + + + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "onStartCommand: Start MQTT service"); + context = getApplicationContext(); + mqttSP = SharedPreferencesGrouper.getInstance(context).getSharedPreference(SPType.mqtt_sp); + deviceName = SharedPreferencesGrouper.getInstance(context).getSharedPreference(SPType.default_sp).getString("device_name", "null").strip(); + startForeground(3, builder.build()); + setupSharedPreferences(); + createClient(); + connectClient(); + + subscribeToAllTopics(); + + return START_STICKY; + } + + private void executeWork() { + new Runnable(){ + @Override + public void run() { + if (iperf3Handler != null && isEnabled) { + iperf3Handler.enableSequence(); + } else { + Log.d(TAG, "executeWork: Iperf3 Handler is either null or not enabled"); + } + } + }.run(); + new Runnable(){ + @Override + public void run() { + if (pingHandler != null && isEnabled) { + pingHandler.enableSequence(); + } else { + Log.d(TAG, "executeWork: Ping Handler is either null or not enabled"); + } + } + }.run(); + + CustomEventListener listener = new CustomEventListener() { + @Override + public void onChange(HashMap workInfos) { + for (WorkInfo info : workInfos.values()) { + WorkInfo.State state = info.getState(); + Log.d(TAG, "onChange: WorkInfo: " + info.getTags() + " State: " + state); + Data data = info.getOutputData(); + Log.i(TAG, "onChange: "+data.toString()); + + publishToTopic("device/"+deviceName+"/campaign/status", String.valueOf(state.ordinal()), false); + } + + } + }; + //startWorkInfoChecker(RemoteWorkManager.getInstance(context), iperf3Handler.getExecutorWorkRequests(context), listener); + //TODO + } + + private void startWorkInfoChecker(RemoteWorkManager remoteWorkManager, ArrayList workRequests, CustomEventListener listener) { + ArrayList workIdGroups = new ArrayList<>(); + for (OneTimeWorkRequest workRequest : workRequests) { + workIdGroups.add(workRequest.getId()); + } + RemoteWorkInfoChecker remoteWorkInfoChecker = new RemoteWorkInfoChecker(remoteWorkManager, workIdGroups); + remoteWorkInfoChecker.setListener(listener); + remoteWorkInfoChecker.start(); + } + + private void setEnabled(boolean isEnabled) { + this.isEnabled = isEnabled; + executeWork(); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MainActivity.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MainActivity.java index e3d0fcec..6fc6a1bb 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MainActivity.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/MainActivity.java @@ -57,6 +57,7 @@ import de.fraunhofer.fokus.OpenMobileNetworkToolkit.DataProvider.DataProvider; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.DataProvider.NetworkCallback; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.MQTT.MQTTService; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.WorkProfile.WorkProfileActivity; @@ -70,6 +71,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen public boolean cp = false; public boolean feature_telephony = false; Intent loggingServiceIntent; + Intent mqttServiceIntent; Intent notificationServiceIntent; NavController navController; private Handler requestCellInfoUpdateHandler; @@ -231,6 +233,24 @@ protected void onCreate(Bundle savedInstanceState) { } }, SPType.default_sp); + mqttServiceIntent = new Intent(this, MQTTService.class); + if (spg.getSharedPreference(SPType.mqtt_sp).getBoolean("enable_mqtt", false)) { + Log.d(TAG, "Start MQTT service"); + context.startService(mqttServiceIntent); + } + + spg.setListener((prefs, key) -> { + if (Objects.equals(key, "enable_mqtt")) { + if (prefs.getBoolean(key, false)) { + Log.d(TAG, "MQTT enabled"); + context.startForegroundService(mqttServiceIntent); + } else { + Log.d(TAG, "MQTT disabled"); + context.stopService(mqttServiceIntent); + } + } + }, SPType.mqtt_sp); + getAppSignature(); gv.setGit_hash(getString(R.string.git_hash)); } @@ -493,6 +513,9 @@ public boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat calle case "shared_preferences_io": navController.navigate(R.id.fragment_shared_preferences_io); break; + case "mqtt_settings": + navController.navigate(R.id.mqttSettingsFragment); + break; } return true; } @@ -503,4 +526,4 @@ private void initHandlerAndHandlerThread() { requestCellInfoUpdateHandler = new Handler(Objects.requireNonNull(requestCellInfoUpdateHandlerThread.getLooper())); requestCellInfoUpdateHandler.post(requestCellInfoUpdate); } -} +} \ No newline at end of file diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/Metric.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/Metric.java deleted file mode 100644 index 4c82f94f..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/Metric.java +++ /dev/null @@ -1,240 +0,0 @@ -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric; - -import android.content.Context; -import android.graphics.Typeface; -import android.graphics.drawable.GradientDrawable; -import android.view.Gravity; -import android.widget.LinearLayout; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.Locale; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; - -public class Metric { - private LinearLayout mean; - private LinearLayout median; - private LinearLayout max; - private LinearLayout min; - private LinearLayout last; - private TextView directionName; - private ArrayList meanList = new ArrayList<>(); - private double maxValueSum = Double.MIN_VALUE; - private double minValueSum = Double.MAX_VALUE; - private final METRIC_TYPE metricType; - private final Context ct; - private LinearLayout mainLL; - - public Metric(METRIC_TYPE metricType, Context ct){ - this.metricType = metricType; - this.ct = ct; - } - - private LinearLayout createTile(String key) { - LinearLayout ll = new LinearLayout(ct); - - GradientDrawable gd = new GradientDrawable(); - gd.setColor(ct.getColor(R.color.cardview_dark_background)); - gd.setCornerRadius(10); - gd.setStroke(2, 0xFF000000); - ll.setBackground(gd); - ll.setMinimumHeight(ll.getWidth()); - ll.setGravity(Gravity.CENTER); - - ll.setOrientation(LinearLayout.VERTICAL); - LinearLayout.LayoutParams foo = new LinearLayout.LayoutParams(200, 150); - foo.weight = 1; - foo.setMargins(10, 10, 10, 10); - ll.setLayoutParams(foo); - TextView keyView = new TextView(ct); - keyView.setGravity(Gravity.CENTER); - LinearLayout.LayoutParams keyViewLayoutParams = new LinearLayout.LayoutParams(200, 50); - keyViewLayoutParams.setMargins(0, 0, 0, 10); - keyView.setLayoutParams(keyViewLayoutParams); - keyView.setTypeface(null, Typeface.BOLD); - - keyView.setText(key); - ll.addView(keyView); - TextView valueView = new TextView(ct); - valueView.setGravity(Gravity.CENTER); - LinearLayout.LayoutParams valueViewLayoutParams = new LinearLayout.LayoutParams(200, 50); - valueViewLayoutParams.setMargins(0, 0, 0, 0); - valueView.setLayoutParams(valueViewLayoutParams); - ll.addView(valueView); - return ll; - } - - private LinearLayout createLL(String key) { - LinearLayout ll = null; - switch (key) { - case "mean": - mean = createTile(key); - ll = mean; - break; - case "median": - median = createTile(key); - ll = median; - break; - case "max": - max = createTile(key); - ll = max; - break; - case "min": - min = createTile(key); - ll = min; - break; - case "last": - last = createTile(key); - ll = last; - break; - } - return ll; - } - private String getFormatedString(double value){ - switch (this.metricType){ - case THROUGHPUT: - return String.format(Locale.getDefault(), "%.2f", value/1e+6); - case RTT: - case PACKET_LOSS: - case JITTER: - case PING_RTT: - case PING_PACKET_LOSS: - return String.format(Locale.getDefault(), "%.2f", value); - } - return Double.toString(value); - } - public LinearLayout createMainLL(String direction) { - mainLL = new LinearLayout(ct); - LinearLayout.LayoutParams foo1 = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ); - mainLL.setOrientation(LinearLayout.VERTICAL); - mainLL.setLayoutParams(foo1); - - directionName = new TextView(ct); - directionName.setText(direction); - mainLL.addView(directionName); - - LinearLayout cardViewResult = new LinearLayout(ct); - LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ); - cardViewResult.setOrientation(LinearLayout.HORIZONTAL); - cardViewResult.setLayoutParams(cardParams); - - cardViewResult.addView(createLL("mean")); - cardViewResult.addView(createLL("median")); - cardViewResult.addView(createLL("max")); - cardViewResult.addView(createLL("min")); - cardViewResult.addView(createLL("last")); - mainLL.addView(cardViewResult); - return mainLL; - } - - public double calcMean(){ - return meanList.stream().mapToDouble(a -> a).sum()/meanList.size(); - } - - public double calcMedian(){ - this.getMeanList().sort(Double::compareTo); - return meanList.get(Math.round(meanList.size()/2)); - } - - public double calcMax(){ - return meanList.stream().mapToDouble(a -> a).max().getAsDouble(); - } - - public double calcMin(){ - return meanList.stream().mapToDouble(a -> a).min().getAsDouble(); - } - public void update(Double value){ - this.meanList.add(value); - - ((TextView)mean.getChildAt(1)).setText(String.format(" %s", getFormatedString(calcMean()))); - ((TextView)median.getChildAt(1)).setText(String.format(" %s", getFormatedString(calcMedian()))); - ((TextView)max.getChildAt(1)).setText(String.format(" %s", getFormatedString(calcMax()))); - ((TextView)min.getChildAt(1)).setText(String.format(" %s", getFormatedString(calcMin()))); - ((TextView)last.getChildAt(1)).setText(String.format(" %s", getFormatedString(value))); - } - - public ArrayList getMeanList() { - return meanList; - } - public void setMaxValueSum(double maxValueSum) { - this.maxValueSum = maxValueSum; - } - public void setMinValueSum(double minValueSum) { - this.minValueSum = minValueSum; - } - public double getMaxValueSum() { - return maxValueSum; - } - public double getMinValueSum() { - return minValueSum; - } - - public void setMeanList(ArrayList meanList) { - this.meanList = meanList; - } - - public LinearLayout getMean() { - return mean; - } - - public void setMean(LinearLayout mean) { - this.mean = mean; - } - - public LinearLayout getMedian() { - return median; - } - - public void setMedian(LinearLayout median) { - this.median = median; - } - - public LinearLayout getMax() { - return max; - } - - public void setMax(LinearLayout max) { - this.max = max; - } - - public LinearLayout getMin() { - return min; - } - - public void setMin(LinearLayout min) { - this.min = min; - } - - public LinearLayout getLast() { - return last; - } - - public void setLast(LinearLayout last) { - this.last = last; - } - - - public TextView getDirectionName() { - return directionName; - } - - public void setDirectionName(TextView directionName) { - this.directionName = directionName; - } - - public void resetMetric(){ - meanList.clear(); - this.maxValueSum = Double.MIN_VALUE; - this.minValueSum = Double.MAX_VALUE; - } - public void setVisibility(int visibility){ - mainLL.setVisibility(visibility); - } - } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricCalculator.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricCalculator.java new file mode 100644 index 00000000..e6f7ccbd --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricCalculator.java @@ -0,0 +1,119 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric; + +import java.util.ArrayList; +import java.util.Locale; + +public class MetricCalculator { + private ArrayList meanList = new ArrayList<>(); + private double maxValueSum = Double.MIN_VALUE; + private double minValueSum = Double.MAX_VALUE; + private final METRIC_TYPE metricType; + + private double median; + private double mean; + private double max; + private double min; + private double last; + + public MetricCalculator(METRIC_TYPE metricType) { + this.metricType = metricType; + } + + public METRIC_TYPE getMetricType() { + return metricType; + } + + public double getMedian() { + return median; + } + + public double getMean() { + return mean; + } + + public double getMax() { + return max; + } + + public double getMin() { + return min; + } + + public double getLast() { + return last; + } + + public void calcAll(){ + if(meanList.isEmpty()){ + return; + } + calcMin(); + calcMax(); + calcMedian(); + calcMean(); + } + + private void calcMean() { + mean = meanList.stream().mapToDouble(a -> a).sum() / meanList.size(); + } + + private void calcMedian() { + this.meanList.sort(Double::compareTo); + median = meanList.get(Math.round(meanList.size() / 2)); + } + + private void calcMax() { + max = meanList.stream().mapToDouble(a -> a).max().getAsDouble(); + } + + private void calcMin() { + min = meanList.stream().mapToDouble(a -> a).min().getAsDouble(); + + } + + public void update(Double value) { + this.meanList.add(value); + this.last = value; + } + + public String getFormattedString(double value) { + switch (this.metricType) { + case THROUGHPUT: + return String.format(Locale.getDefault(), "%.2f", value / 1e+6); + case RTT: + case PACKET_LOSS: + case JITTER: + case PING_RTT: + case PING_PACKET_LOSS: + return String.format(Locale.getDefault(), "%.2f", value); + } + return Double.toString(value); + } + + public ArrayList getMeanList() { + return meanList; + } + + public void resetMetric() { + meanList.clear(); + this.maxValueSum = Double.MIN_VALUE; + this.minValueSum = Double.MAX_VALUE; + } + + public void setMaxValueSum(double maxValueSum) { + this.maxValueSum = maxValueSum; + } + + public void setMinValueSum(double minValueSum) { + this.minValueSum = minValueSum; + } + + public double getMaxValueSum() { + return maxValueSum; + } + + public double getMinValueSum() { + return minValueSum; + } + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricView.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricView.java new file mode 100644 index 00000000..5215de77 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Metric/MetricView.java @@ -0,0 +1,169 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric; + +import static android.widget.LinearLayout.HORIZONTAL; +import static android.widget.LinearLayout.VERTICAL; + +import android.content.Context; +import android.graphics.Typeface; +import android.graphics.drawable.GradientDrawable; +import android.util.AttributeSet; +import android.view.Gravity; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.cardview.widget.CardView; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; + +public class MetricView extends CardView { + + private LinearLayout mean; + private LinearLayout median; + private LinearLayout max; + private LinearLayout min; + private LinearLayout last; + private TextView directionName; + private MetricCalculator metricCalculator; + + public MetricView(Context context) { + super(context); + init(context, null); + } + public MetricView(MetricCalculator metricCalculator, Context context) { + super(context); + init(context, null); + this.metricCalculator = metricCalculator; + } + public MetricView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MetricView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, @Nullable AttributeSet attrs) { + // You may want to customize view via XML attributes if needed. + } + + public void setup(String title) { + + removeAllViews(); + LinearLayout cardView = new LinearLayout(getContext()); + cardView.setOrientation(VERTICAL); + + directionName = new TextView(getContext()); + directionName.setText(title); + directionName.setTextAppearance(R.style.Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Large); + directionName.setTextColor(getContext().getColor(R.color.material_dynamic_secondary100)); + cardView.addView(directionName); + + LinearLayout cardViewResult = new LinearLayout(getContext()); + cardViewResult.setOrientation(HORIZONTAL); + cardViewResult.setLayoutParams(new LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT + )); + + mean = createTile("mean", 0, 10); + median = createTile("median", 10, 10); + max = createTile("max", 10, 10); + min = createTile("min", 10, 10); + last = createTile("last", 10, 0); + + cardViewResult.addView(mean); + cardViewResult.addView(median); + cardViewResult.addView(max); + cardViewResult.addView(min); + cardViewResult.addView(last); + + cardView.addView(cardViewResult); + addView(cardView); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT + ); + cardView.setPadding(20, 20, 20 ,20); + + + GradientDrawable gd = new GradientDrawable(); + gd.setColor(getContext().getColor( R.color.material_dynamic_secondary30)); + gd.setCornerRadius(10); +// gd.setStroke(2, 0xFF000000); + this.setBackground(gd); + + params.setMargins(20 , 10, 20, 10); + this.setLayoutParams(params); + } + + private LinearLayout createTile(String key, int marginLeft, int marginRight) { + Context ct = getContext(); + LinearLayout ll = new LinearLayout(ct); + ll.setOrientation(VERTICAL); + ll.setGravity(Gravity.CENTER); + + GradientDrawable gd = new GradientDrawable(); + gd.setColor(ct.getColor(R.color.material_dynamic_primary100)); + gd.setCornerRadius(10); + gd.setStroke(2, 0xFF000000); + ll.setBackground(gd); + + LinearLayout.LayoutParams tileParams = new LinearLayout.LayoutParams(200, 150); + tileParams.weight = 1; + tileParams.setMargins(marginLeft, 10, marginRight, 10); + ll.setLayoutParams(tileParams); + + TextView keyView = new TextView(ct); + keyView.setGravity(Gravity.CENTER); + keyView.setText(key); + LinearLayout.LayoutParams keyViewParams = new LinearLayout.LayoutParams(200, 0); + keyViewParams.weight = 1; + keyViewParams.setMargins(0, 0, 0, 10); + keyView.setLayoutParams(keyViewParams); + keyView.setTypeface(null, Typeface.BOLD); + keyView.setTextAppearance(R.style.Base_TextAppearance_AppCompat_Medium); + keyView.setTextColor(ct.getColor(R.color.material_dynamic_primary10)); + keyView.setGravity(Gravity.CENTER); + + + TextView valueView = new TextView(ct); + valueView.setGravity(Gravity.CENTER); + LinearLayout.LayoutParams valueViewParams = new LinearLayout.LayoutParams(200, 0); + valueViewParams.weight = 1; + valueView.setLayoutParams(valueViewParams); + valueView.setTextAppearance(R.style.TextAppearance_AppCompat_Small); + valueView.setTextColor(ct.getColor(R.color.material_dynamic_primary10)); + + + ll.addView(keyView); + ll.addView(valueView); + return ll; + } + + public void update(Double value) { + if (metricCalculator == null) return; + + metricCalculator.update(value); + update(); + } + + public void update(){ + metricCalculator.calcAll(); + ((TextView) mean.getChildAt(1)).setText(metricCalculator.getFormattedString(metricCalculator.getMean())); + ((TextView) median.getChildAt(1)).setText(metricCalculator.getFormattedString(metricCalculator.getMedian())); + ((TextView) max.getChildAt(1)).setText(metricCalculator.getFormattedString(metricCalculator.getMax())); + ((TextView) min.getChildAt(1)).setText(metricCalculator.getFormattedString(metricCalculator.getMin())); + ((TextView) last.getChildAt(1)).setText(metricCalculator.getFormattedString(metricCalculator.getLast())); + } + + public void setMetricCalculator(MetricCalculator metricCalculator) { + this.metricCalculator = metricCalculator; + } + + public MetricCalculator getMetricCalculator() { + return metricCalculator; + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Iperf3Parameter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Iperf3Parameter.java new file mode 100644 index 00000000..aaef802e --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Iperf3Parameter.java @@ -0,0 +1,1810 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter; + +import android.os.Environment; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; + +public class Iperf3Parameter extends Parameter { + public static final String rootPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(); + public static final String rawDirPath = rootPath+"/omnt/iperf3/raw/"; + public static final String lineProtocolDirPath = rootPath+"/omnt/iperf3/lineprotocol/"; + public static final String HOST = "host"; + public static final String PORT = "port"; + public static final String BITRATE = "bitrate"; + public static final String INTERVAL = "interval"; + public static final String BYTES = "bytes"; + public static final String STREAMS = "streams"; + public static final String DIRECTION = "direction"; + public static final String ONEOFF = "oneOff"; + public static final String CPORT = "cport"; + public static final String TESTUUID = "testUUID"; + public static final String USERNAME = "username"; + public static final String RSAPUBLICKEYPATH = "rsaPublicKeyPath"; + public static final String EXTRADATA = "extraData"; + public static final String TITLE = "title"; + public static final String OMIT = "omit"; + public static final String ZEROCOPY = "zerocopy"; + public static final String FLOWLABEL = "flowlabel"; + public static final String DSCP = "dscp"; + public static final String TOS = "tos"; + public static final String VERSION6 = "version6"; + public static final String VERSION4 = "version4"; + public static final String NODELAY = "noDelay"; + public static final String SETMSS = "setMss"; + public static final String CONGESTION = "congestion"; + public static final String WINDOW = "window"; + public static final String PARALLEL = "parallel"; + public static final String BLOCKCOUNT = "blockcount"; + public static final String TIME = "time"; + public static final String FQRATE = "fqRate"; + public static final String PACINGTIMER = "pacingTimer"; + public static final String CONNECTTIMEOUT = "connectTimeout"; + public static final String UDP = "udp"; + public static final String XBIND = "xbind"; + public static final String SCTP = "sctp"; + public static final String USEPKCS1PADDING = "usePkcs1Padding"; + public static final String TIMESKEWTHRESHOLD = "timeSkewThreshold"; + public static final String AUTHORIZEDUSERSPATH = "authorizedUsersPath"; + public static final String RSAPRIVATEKEYPATH = "rsaPrivateKeyPath"; + public static final String IDLETIMEOUT = "idleTimeout"; + public static final String SERVERBITRATELIMIT = "serverBitrateLimit"; + + public static final String SERVER = "server"; + public static final String CLIENT = "client"; + public static final String DAEMON = "daemon"; + public static final String HELP = "help"; + public static final String VERSION = "version"; + public static final String DEBUG = "debug"; + public static final String SNDTIMEOUT = "sndTimeout"; + public static final String RCVTIMEOUT = "rcvTimeout"; + public static final String TIMESTAMPS = "timestamps"; + public static final String FORCEFLUSH = "forceflush"; + public static final String LOGFILE = "logfile"; + + public static final String JSON = "json"; + public static final String VERBOSE = "verbose"; + public static final String BINDDEV = "bindDev"; + public static final String BIND = "bind"; + public static final String AFFINITY = "affinity"; + public static final String FILE = "file"; + public static final String MODE = "mode"; + public static final String PROTOCOL = "protocol"; + public static final String IPERF3UUID = "iperf3UUID"; + + private static final String TAG = "Iperf3Parameter"; + + + protected Iperf3Parameter(Parcel in) { + super(in); + host = in.readString(); + iPerf3UUID = in.readString(); + port = in.readInt(); + interval = in.readDouble(); + bitrate = in.readString(); + length = in.readInt(); + pidfile = in.readString(); + file = in.readString(); + affinity = in.readString(); + bind = in.readString(); + bindDev = in.readString(); + byte tmpVerbose = in.readByte(); + verbose = tmpVerbose == 0 ? null : tmpVerbose == 1; + byte tmpJson = in.readByte(); + json = tmpJson == 0 ? null : tmpJson == 1; + byte tmpJsonStream = in.readByte(); + jsonStream = tmpJsonStream == 0 ? null : tmpJsonStream == 1; + byte tmpForceflush = in.readByte(); + forceflush = tmpForceflush == 0 ? null : tmpForceflush == 1; + timestamps = in.readString(); + if (in.readByte() == 0) { + rcvTimeout = null; + } else { + rcvTimeout = in.readInt(); + } + if (in.readByte() == 0) { + sndTimeout = null; + } else { + sndTimeout = in.readInt(); + } + if (in.readByte() == 0) { + debug = null; + } else { + debug = in.readInt(); + } + byte tmpVersion = in.readByte(); + version = tmpVersion == 0 ? null : tmpVersion == 1; + byte tmpHelp = in.readByte(); + help = tmpHelp == 0 ? null : tmpHelp == 1; + byte tmpDaemon = in.readByte(); + daemon = tmpDaemon == 0 ? null : tmpDaemon == 1; + byte tmpOneOff = in.readByte(); + oneOff = tmpOneOff == 0 ? null : tmpOneOff == 1; + serverBitrateLimit = in.readString(); + if (in.readByte() == 0) { + idleTimeout = null; + } else { + idleTimeout = in.readInt(); + } + rsaPrivateKeyPath = in.readString(); + authorizedUsersPath = in.readString(); + if (in.readByte() == 0) { + timeSkewThreshold = null; + } else { + timeSkewThreshold = in.readInt(); + } + byte tmpUsePkcs1Padding = in.readByte(); + usePkcs1Padding = tmpUsePkcs1Padding == 0 ? null : tmpUsePkcs1Padding == 1; + byte tmpSctp = in.readByte(); + sctp = tmpSctp == 0 ? null : tmpSctp == 1; + xbind = in.readString(); + if (in.readByte() == 0) { + nstreams = null; + } else { + nstreams = in.readInt(); + } + if (in.readByte() == 0) { + connectTimeout = null; + } else { + connectTimeout = in.readInt(); + } + pacingTimer = in.readString(); + fqRate = in.readString(); + if (in.readByte() == 0) { + time = null; + } else { + time = in.readInt(); + } + bytes = in.readString(); + blockcount = in.readString(); + if (in.readByte() == 0) { + cport = null; + } else { + cport = in.readInt(); + } + if (in.readByte() == 0) { + parallel = null; + } else { + parallel = in.readInt(); + } + byte tmpReverse = in.readByte(); + reverse = tmpReverse == 0 ? null : tmpReverse == 1; + byte tmpBidir = in.readByte(); + bidir = tmpBidir == 0 ? null : tmpBidir == 1; + window = in.readString(); + congestion = in.readString(); + if (in.readByte() == 0) { + setMss = null; + } else { + setMss = in.readInt(); + } + byte tmpNoDelay = in.readByte(); + noDelay = tmpNoDelay == 0 ? null : tmpNoDelay == 1; + byte tmpVersion4 = in.readByte(); + version4 = tmpVersion4 == 0 ? null : tmpVersion4 == 1; + byte tmpVersion6 = in.readByte(); + version6 = tmpVersion6 == 0 ? null : tmpVersion6 == 1; + if (in.readByte() == 0) { + tos = null; + } else { + tos = in.readInt(); + } + dscp = in.readString(); + if (in.readByte() == 0) { + flowlabel = null; + } else { + flowlabel = in.readInt(); + } + byte tmpZerocopy = in.readByte(); + zerocopy = tmpZerocopy == 0 ? null : tmpZerocopy == 1; + if (in.readByte() == 0) { + omit = null; + } else { + omit = in.readInt(); + } + title = in.readString(); + extraData = in.readString(); + byte tmpGetServerOutput = in.readByte(); + getServerOutput = tmpGetServerOutput == 0 ? null : tmpGetServerOutput == 1; + byte tmpUdpCounters64bit = in.readByte(); + udpCounters64bit = tmpUdpCounters64bit == 0 ? null : tmpUdpCounters64bit == 1; + byte tmpRepeatingPayload = in.readByte(); + repeatingPayload = tmpRepeatingPayload == 0 ? null : tmpRepeatingPayload == 1; + byte tmpDontFragment = in.readByte(); + dontFragment = tmpDontFragment == 0 ? null : tmpDontFragment == 1; + username = in.readString(); + rsaPublicKeyPath = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Iperf3Parameter createFromParcel(Parcel in) { + return new Iperf3Parameter(in); + } + + @Override + public Iperf3Parameter[] newArray(int size) { + return new Iperf3Parameter[size]; + } + }; + + public Iperf3Parameter(String ip, + int port, + String bitrate, + int duration, + double interval, + String bytes, + int streams, + int cport, + String testUUID, + Iperf3Mode mode, + Iperf3Protocol protocol, + Iperf3Direction direction + ) { + + super(rawDirPath+testUUID+".txt", lineProtocolDirPath+testUUID+".txt"); + this.testUUID = testUUID; + this.host = ip; + this.port = port; + this.bitrate = bitrate; + this.time = duration; + this.interval = interval; + this.bytes = bytes; + this.nstreams = streams; + this.cport = cport; + this.mode = mode; + this.direction = direction; + this.protocol = protocol; + + } + + public Iperf3Parameter(String iPerf3UUID){ + super(rawDirPath+iPerf3UUID+".txt", lineProtocolDirPath+iPerf3UUID+".txt"); + } + public Iperf3Parameter(String ip, + String iPerf3UUID, + Iperf3Protocol protocol, + int port, + double interval, + String bitrate, + int length, + Iperf3Mode mode, + Iperf3Direction direction, + String pidfile, + String file, + String affinity, + String bind, + String bindDev, + Boolean verbose, + Boolean json, + Boolean jsonStream, + String logfile, + Boolean forceflush, + String timestamps, + Integer rcvTimeout, + Integer sndTimeout, + Integer debug, + Boolean version, + Boolean help, + Boolean daemon, + Boolean oneOff, + String serverBitrateLimit, + Integer idleTimeout, + String rsaPrivateKeyPath, + String authorizedUsersPath, + Integer timeSkewThreshold, + Boolean usePkcs1Padding, + Boolean sctp, + String xbind, + Integer nstreams, + Integer connectTimeout, + String pacingTimer, + String fqRate, + Integer time, + String bytes, + String blockcount, + Integer cport, + Integer parallel, + Boolean reverse, + Boolean bidir, + String window, + String congestion, + Integer setMss, + Boolean noDelay, + Boolean version4, + Boolean version6, + Integer tos, + String dscp, + Integer flowlabel, + Boolean zerocopy, + Integer omit, + String title, + String extraData, + Boolean getServerOutput, + Boolean udpCounters64bit, + Boolean repeatingPayload, + Boolean dontFragment, + String username, + String rsaPublicKeyPath) { + super(rawDirPath+iPerf3UUID+".txt", lineProtocolDirPath+iPerf3UUID+".txt"); + this.host = ip; + this.iPerf3UUID = iPerf3UUID; + this.protocol = protocol; + this.port = port; + this.interval = interval; + this.bitrate = bitrate; + this.length = length; + this.mode = mode; + this.direction = direction; + this.pidfile = pidfile; + this.file = file; + this.affinity = affinity; + this.bind = bind; + this.bindDev = bindDev; + this.verbose = verbose; + this.json = json; + this.jsonStream = jsonStream; + this.forceflush = forceflush; + this.timestamps = timestamps; + this.rcvTimeout = rcvTimeout; + this.sndTimeout = sndTimeout; + this.debug = debug; + this.version = version; + this.help = help; + this.daemon = daemon; + this.oneOff = oneOff; + this.serverBitrateLimit = serverBitrateLimit; + this.idleTimeout = idleTimeout; + this.rsaPrivateKeyPath = rsaPrivateKeyPath; + this.authorizedUsersPath = authorizedUsersPath; + this.timeSkewThreshold = timeSkewThreshold; + this.usePkcs1Padding = usePkcs1Padding; + this.sctp = sctp; + this.xbind = xbind; + this.nstreams = nstreams; + this.connectTimeout = connectTimeout; + this.pacingTimer = pacingTimer; + this.fqRate = fqRate; + this.time = time; + this.bytes = bytes; + this.blockcount = blockcount; + this.cport = cport; + this.parallel = parallel; + this.reverse = reverse; + this.bidir = bidir; + this.window = window; + this.congestion = congestion; + this.setMss = setMss; + this.noDelay = noDelay; + this.version4 = version4; + this.version6 = version6; + this.tos = tos; + this.dscp = dscp; + this.flowlabel = flowlabel; + this.zerocopy = zerocopy; + this.omit = omit; + this.title = title; + this.extraData = extraData; + this.getServerOutput = getServerOutput; + this.udpCounters64bit = udpCounters64bit; + this.repeatingPayload = repeatingPayload; + this.dontFragment = dontFragment; + this.username = username; + this.rsaPublicKeyPath = rsaPublicKeyPath; + } + + public void updatePaths(){ + super.setLogfile(rawDirPath+testUUID+".txt"); + super.setLineProtocolFile(lineProtocolDirPath+testUUID+".txt"); + } + public Iperf3Parameter(JSONObject jsonObject, String testUUID) { + super(rawDirPath+testUUID+".txt", lineProtocolDirPath+testUUID+".txt"); + + this.testUUID = testUUID; + this.jsonStream = true; + + try { + this.host = jsonObject.getString(HOST); + } catch (JSONException e) { + return; + } + try { + this.iPerf3UUID = jsonObject.getString(IPERF3UUID); + } catch (JSONException e) { + throw new IllegalArgumentException("Iperf3Parameter: iPerf3UUID is required."); + } + try { + this.port = jsonObject.getInt(PORT); + } catch (JSONException e) { + Log.d(TAG, "port is not set. Defaulting to iPerf3 default Port."); + } + try { + this.bitrate = jsonObject.getString(BITRATE); + } catch (JSONException e) { + Log.d(TAG, "bitrate is not set. Defaulting to iPerf3 default bitrate."); + } + try { + this.time = jsonObject.getInt(TIME); + Log.d(TAG, "Iperf3Parameter: time: "+time); + } catch (JSONException e) { + Log.d(TAG, "time is not set. Defaulting to iPerf3 default time."); + } + + try { + this.interval = jsonObject.getDouble(INTERVAL); + } catch (JSONException e) { + Log.d(TAG, "interval is not set. Defaulting to iPerf3 default interval."); + } + try { + this.length = jsonObject.getInt(BYTES); + } catch (JSONException e) { + Log.d(TAG, "Length not set."); + } + try { + this.nstreams = jsonObject.getInt(STREAMS); + } catch (JSONException e) { + Log.d(TAG, "nstreams not set."); + } + try { + String direction = jsonObject.getString(DIRECTION); + Log.d(TAG, "Iperf3Parameter: direction: "+direction); + this.direction = Iperf3Direction.valueOf(direction.toUpperCase().trim()); + } catch (JSONException e) { + this.direction = Iperf3Direction.UP; + Log.d(TAG, "direction not set."); + } + try { + this.oneOff = jsonObject.getBoolean(ONEOFF); + } catch (JSONException e) { + Log.d(TAG, "oneOff not set."); + } + try { + this.protocol = Iperf3Protocol.valueOf(jsonObject.getString(PROTOCOL).toUpperCase().trim()); + } catch (JSONException | IllegalArgumentException e) { + Log.d(TAG, "protocol not set."+e.getMessage()); + Log.e(TAG, "Iperf3Parameter: No matching Protocol found! Using TCP as default."); + this.protocol = Iperf3Protocol.TCP; + } + try { + boolean isServer = false, isClient = false; + try { + // check if server + isServer = jsonObject.getBoolean(SERVER); + } catch (JSONException e) { + Log.d(TAG, "mode not set."); + } + try { + //check if client + jsonObject.getString(HOST); + isClient = true; + } catch (JSONException e) { + isClient = false; + Log.d(TAG, "mode not set."); + } + // check if both are set + if(isServer && isClient){ + throw new IllegalArgumentException("Iperf3Parameter: Server and Client mode cannot be set at the same time."); + } + // check if none is set + if(isServer){ + this.mode = Iperf3Mode.SERVER; + } else if(isClient){ + this.mode = Iperf3Mode.CLIENT; + } else { + throw new IllegalArgumentException("Iperf3Parameter: Server or Client mode must be set."); + } + } catch (IllegalArgumentException e) { + Log.d(TAG, "mode not set."); + Log.e(TAG, e.getMessage()); + } + try { + this.cport = jsonObject.getInt(CPORT); + } catch (JSONException e) { + Log.d(TAG, "cport not set."); + } + + try{ + this.username = jsonObject.getString(USERNAME); + } catch (JSONException e) { + Log.d(TAG, "username not set."); + } + try{ + this.rsaPublicKeyPath = jsonObject.getString(RSAPUBLICKEYPATH); + } catch (JSONException e) { + Log.d(TAG, "rsaPublicKeyPath not set."); + } + try{ + this.extraData = jsonObject.getString(EXTRADATA); + } catch (JSONException e) { + Log.d(TAG, "extraData not set."); + } + try{ + this.title = jsonObject.getString(TITLE); + } catch (JSONException e) { + Log.d(TAG, "title not set."); + } + try{ + this.omit = jsonObject.getInt(OMIT); + } catch (JSONException e) { + Log.d(TAG, "omit not set."); + } + try{ + this.zerocopy = jsonObject.getBoolean(ZEROCOPY); + } catch (JSONException e) { + Log.d(TAG, "zerocopy not set."); + } + try{ + this.flowlabel = jsonObject.getInt(FLOWLABEL); + } catch (JSONException e) { + Log.d(TAG, "flowlabel not set."); + } + try{ + this.dscp = jsonObject.getString(DSCP); + } catch (JSONException e) { + Log.d(TAG, "dscp not set."); + } + try{ + this.tos = jsonObject.getInt(TOS); + } catch (JSONException e) { + Log.d(TAG, "tos not set."); + } + try{ + this.version6 = jsonObject.getBoolean(VERSION6); + } catch (JSONException e) { + Log.d(TAG, "version6 not set."); + } + try{ + this.version4 = jsonObject.getBoolean(VERSION4); + } catch (JSONException e) { + Log.d(TAG, "version4 not set."); + } + try{ + this.noDelay = jsonObject.getBoolean(NODELAY); + } catch (JSONException e) { + Log.d(TAG, "noDelay not set."); + } + try{ + this.setMss = jsonObject.getInt(SETMSS); + } catch (JSONException e) { + Log.d(TAG, "setMss not set."); + } + try{ + this.congestion = jsonObject.getString(CONGESTION); + } catch (JSONException e) { + Log.d(TAG, "congestion not set."); + } + try{ + this.window = jsonObject.getString(WINDOW); + } catch (JSONException e) { + Log.d(TAG, "window not set."); + } + + try{ + this.parallel = jsonObject.getInt(PARALLEL); + } catch (JSONException e) { + Log.d(TAG, "parallel not set."); + } + try{ + this.blockcount = jsonObject.getString(BLOCKCOUNT); + } catch (JSONException e) { + Log.d(TAG, "blockcount not set."); + } + try{ + this.bytes = jsonObject.getString(BYTES); + } catch (JSONException e) { + Log.d(TAG, "bytes not set."); + } + try{ + this.fqRate = jsonObject.getString(FQRATE); + } catch (JSONException e) { + Log.d(TAG, "fqRate not set."); + } + try{ + this.pacingTimer = jsonObject.getString(PACINGTIMER); + } catch (JSONException e) { + Log.d(TAG, "pacingTimer not set."); + } + try{ + this.connectTimeout = jsonObject.getInt(CONNECTTIMEOUT); + } catch (JSONException e) { + Log.d(TAG, "connectTimeout not set."); + } + try{ + this.xbind = jsonObject.getString(XBIND); + } catch (JSONException e) { + Log.d(TAG, "xbind not set."); + } + try{ + this.sctp = jsonObject.getBoolean(SCTP); + } catch (JSONException e) { + Log.d(TAG, "sctp not set."); + } + try{ + this.usePkcs1Padding = jsonObject.getBoolean(USEPKCS1PADDING); + } catch (JSONException e) { + Log.d(TAG, "usePkcs1Padding not set."); + } + try{ + this.timeSkewThreshold = jsonObject.getInt(TIMESKEWTHRESHOLD); + } catch (JSONException e) { + Log.d(TAG, "timeSkewThreshold not set."); + } + try{ + this.authorizedUsersPath = jsonObject.getString(AUTHORIZEDUSERSPATH); + } catch (JSONException e) { + Log.d(TAG, "authorizedUsersPath not set."); + } + try{ + this.rsaPrivateKeyPath = jsonObject.getString(RSAPRIVATEKEYPATH); + } catch (JSONException e) { + Log.d(TAG, "rsaPrivateKeyPath not set."); + } + try{ + this.idleTimeout = jsonObject.getInt(IDLETIMEOUT); + } catch (JSONException e) { + Log.d(TAG, "idleTimeout not set."); + } + try{ + this.serverBitrateLimit = jsonObject.getString(SERVERBITRATELIMIT); + } catch (JSONException e) { + Log.d(TAG, "serverBitrateLimit not set."); + } + try{ + this.oneOff = jsonObject.getBoolean(ONEOFF); + } catch (JSONException e) { + Log.d(TAG, "oneOff not set."); + } + try{ + this.daemon = jsonObject.getBoolean(DAEMON); + } catch (JSONException e) { + Log.d(TAG, "daemon not set."); + } + try{ + this.help = jsonObject.getBoolean(HELP); + } catch (JSONException e) { + Log.d(TAG, "help not set."); + } + try{ + this.version = jsonObject.getBoolean(VERSION); + } catch (JSONException e) { + Log.d(TAG, "version not set."); + } + try{ + this.debug = jsonObject.getInt(DEBUG); + } catch (JSONException e) { + Log.d(TAG, "debug not set."); + } + try{ + this.sndTimeout = jsonObject.getInt(SNDTIMEOUT); + } catch (JSONException e) { + Log.d(TAG, "sndTimeout not set."); + } + try{ + this.rcvTimeout = jsonObject.getInt(RCVTIMEOUT); + } catch (JSONException e) { + Log.d(TAG, "rcvTimeout not set."); + } + try{ + this.timestamps = jsonObject.getString(TIMESTAMPS); + } catch (JSONException e) { + Log.d(TAG, "timestamps not set."); + } + try{ + this.forceflush = jsonObject.getBoolean(FORCEFLUSH); + } catch (JSONException e) { + Log.d(TAG, "forceflush not set."); + } + + try{ + this.verbose = jsonObject.getBoolean(VERBOSE); + } catch (JSONException e) { + Log.d(TAG, "verbose not set."); + } + try{ + this.bindDev = jsonObject.getString(BINDDEV); + } catch (JSONException e) { + Log.d(TAG, "bindDev not set."); + } + try{ + this.affinity = jsonObject.getString(AFFINITY); + } catch (JSONException e) { + Log.d(TAG, "affinity not set."); + } + try{ + this.file = jsonObject.getString(FILE); + } catch (JSONException e) { + Log.d(TAG, "file not set."); + } + try { + String mode = jsonObject.getString(MODE); + Log.d(TAG, "Iperf3Parameter: mode: "+mode); + this.mode = Iperf3Mode.valueOf(mode.toUpperCase().trim()); + } catch (JSONException e) { + Log.d(TAG, "mode not set."); + Log.i(TAG, "Iperf3Parameter: No mode set. Defaulting to Client."); + this.mode = Iperf3Mode.CLIENT; + } + try{ + this.bind = jsonObject.getString(BIND); + } catch (JSONException e) { + Log.d(TAG, "bind not set."); + } + try{ + this.title = jsonObject.getString(TITLE); + } catch (JSONException e) { + Log.d(TAG, "title not set."); + } + try { + Files.createDirectories(Paths.get(rawDirPath)); + Files.createDirectories(Paths.get(lineProtocolDirPath)); + } catch (IOException e) { + Log.d(TAG, "Could not create directories."); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(host); + dest.writeString(iPerf3UUID); + dest.writeInt(port); + dest.writeDouble(interval); + dest.writeString(bitrate); + dest.writeInt(length); + dest.writeString(mode.name()); + dest.writeString(direction.name()); + dest.writeString(pidfile); + dest.writeString(file); + dest.writeString(affinity); + dest.writeString(bind); + dest.writeString(bindDev); + dest.writeBoolean(verbose); + dest.writeBoolean(json); + dest.writeBoolean(jsonStream); + dest.writeBoolean(forceflush); + dest.writeString(timestamps); + dest.writeInt(rcvTimeout); + dest.writeInt(sndTimeout); + dest.writeInt(debug); + dest.writeBoolean(version); + dest.writeBoolean(help); + dest.writeBoolean(daemon); + dest.writeBoolean(oneOff); + dest.writeString(serverBitrateLimit); + dest.writeInt(idleTimeout); + dest.writeString(rsaPrivateKeyPath); + dest.writeString(authorizedUsersPath); + dest.writeInt(timeSkewThreshold); + dest.writeBoolean(usePkcs1Padding); + dest.writeBoolean(sctp); + dest.writeString(xbind); + dest.writeInt(nstreams); + dest.writeInt(connectTimeout); + dest.writeString(pacingTimer); + dest.writeString(fqRate); + dest.writeInt(time); + dest.writeString(bytes); + dest.writeString(blockcount); + dest.writeInt(cport); + dest.writeInt(parallel); + dest.writeBoolean(reverse); + dest.writeBoolean(bidir); + dest.writeString(window); + dest.writeString(congestion); + dest.writeInt(setMss); + dest.writeBoolean(noDelay); + dest.writeBoolean(version4); + dest.writeBoolean(version6); + dest.writeInt(tos); + dest.writeString(dscp); + dest.writeInt(flowlabel); + dest.writeBoolean(zerocopy); + dest.writeInt(omit); + dest.writeString(title); + dest.writeString(extraData); + dest.writeBoolean(getServerOutput); + dest.writeBoolean(udpCounters64bit); + dest.writeBoolean(repeatingPayload); + dest.writeBoolean(dontFragment); + dest.writeString(username); + dest.writeString(rsaPublicKeyPath); + } + + public String getHost() { + return host; + } + + public void setHost(String s) { + this.host = s; + } + + public void setBandwidth(String s) { + this.bitrate = s ; + } + + + // --- Enums --- + public enum Iperf3Mode { + CLIENT, + SERVER, + UNDEFINED; + public String toPrettyPrint() { + return this.name().substring(0, 1).toUpperCase() + this.name().toLowerCase().substring(1); + } + } + + public enum Iperf3Protocol { + TCP, + UDP, + UNDEFINED; + } + + public enum Iperf3Direction { + UP, + DOWN, + BIDIR, + UNDEFINED; + public String toPrettyPrint() { + return this.name().toLowerCase(); + } + } + + // --- Fields --- + // Required field. + private String host; + private String iPerf3UUID; + private String testUUID; + // Optional fields with defaults based on iperf3. + private Iperf3Protocol protocol = Iperf3Protocol.TCP; // Default protocol: TCP. + private int port = 5201; // Default port: 5201. + private double interval = 1.0; // Default interval: 1.0 second. + // For UDP mode: if not set, default bitrate is "1M". + private String bitrate; + // For TCP mode: if not set, default buffer length is 131072 (128 KB). + private int length; + + // Mode selection fields. + // The mode field indicates whether the test is run in CLIENT or SERVER mode. + // When mode is CLIENT, the client field (host) must be provided. + private Iperf3Mode mode = Iperf3Mode.UNDEFINED; + + + private Iperf3Direction direction = Iperf3Direction.UNDEFINED; + + // Additional optional parameters. + private String pidfile; + private String file; + private String affinity; + private String bind; + private String bindDev; + private Boolean verbose; + private Boolean json; + private Boolean jsonStream = true; + private Boolean forceflush; + private String timestamps; + private Integer rcvTimeout; + private Integer sndTimeout; + private Integer debug; + private Boolean version; + private Boolean help; + private Boolean daemon; + private Boolean oneOff; + private String serverBitrateLimit; + private Integer idleTimeout; + private String rsaPrivateKeyPath; + private String authorizedUsersPath; + private Integer timeSkewThreshold; + private Boolean usePkcs1Padding; + private Boolean sctp; + private String xbind; + private Integer nstreams; + + private Integer connectTimeout; + private String pacingTimer; + private String fqRate; + private Integer time = 10; + private String bytes; + private String blockcount; + private Integer cport; + private Integer parallel; + private Boolean reverse; + private Boolean bidir; + private String window; + private String congestion; + private Integer setMss; + private Boolean noDelay; + private Boolean version4; + private Boolean version6; + private Integer tos; + private String dscp; + private Integer flowlabel; + private Boolean zerocopy; + private Integer omit; + private String title; + private String extraData; + private Boolean getServerOutput; + private Boolean udpCounters64bit; + private Boolean repeatingPayload; + private Boolean dontFragment; + private String username; + private String rsaPublicKeyPath; + + + // --- Getters and Setters --- + + public String getiPerf3UUID() { + return iPerf3UUID; + } + + public void setiPerf3UUID(String iPerf3UUID) { + this.iPerf3UUID = iPerf3UUID; + } + + public Iperf3Protocol getProtocol() { + return protocol; + } + + /** + * Set the protocol. If the input is not null, the protocol is set accordingly. + * Otherwise, it defaults to TCP. + */ + public void setProtocol(Iperf3Protocol protocol) { + this.protocol = (protocol != null) ? protocol : Iperf3Protocol.TCP; + } + public void setTestUUID(String testUUID) { + this.testUUID = testUUID; + } + public int getPort() { + if(port == 0){ + return 5201; + } + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public double getInterval() { + if(interval == 0.0){ + return 1.0; + } + + return interval; + } + + public void setInterval(double interval) { + this.interval = interval; + } + + public String getBitrate() { + return bitrate; + } + + public void setBitrate(String bitrate) { + this.bitrate = bitrate; + } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public Iperf3Mode getMode() { + return mode; + } + + /** + * Set the mode. If the mode is set to SERVER, the client field is cleared. + */ + public void setMode(Iperf3Mode mode) { + this.mode = (mode != null) ? mode : Iperf3Mode.UNDEFINED; + } + + + + public Iperf3Direction getDirection() { + return direction; + } + + public void setDirection(Iperf3Direction direction) { + this.direction = (direction != null) ? direction : Iperf3Direction.UNDEFINED; + } + + // Additional getters and setters for remaining fields... + + public String getPidfile() { + return pidfile; + } + + public void setPidfile(String pidfile) { + this.pidfile = pidfile; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getAffinity() { + return affinity; + } + + public void setAffinity(String affinity) { + this.affinity = affinity; + } + + public String getBind() { + return bind; + } + + public void setBind(String bind) { + this.bind = bind; + } + + public String getBindDev() { + return bindDev; + } + + public void setBindDev(String bindDev) { + this.bindDev = bindDev; + } + + public Boolean getVerbose() { + return verbose; + } + + public void setVerbose(Boolean verbose) { + this.verbose = verbose; + } + + public Boolean getJson() { + return json; + } + + public void setJson(Boolean json) { + this.json = json; + } + + public Boolean getJsonStream() { + return jsonStream; + } + + public void setJsonStream(Boolean jsonStream) { + this.jsonStream = jsonStream; + } + + + public Boolean getForceflush() { + return forceflush; + } + + public void setForceflush(Boolean forceflush) { + this.forceflush = forceflush; + } + + public String getTimestamps() { + return timestamps; + } + + public void setTimestamps(String timestamps) { + this.timestamps = timestamps; + } + + public Integer getRcvTimeout() { + return rcvTimeout; + } + + public void setRcvTimeout(Integer rcvTimeout) { + this.rcvTimeout = rcvTimeout; + } + + public Integer getSndTimeout() { + return sndTimeout; + } + + public void setSndTimeout(Integer sndTimeout) { + this.sndTimeout = sndTimeout; + } + + public Integer getDebug() { + return debug; + } + + public void setDebug(Integer debug) { + this.debug = debug; + } + + public Boolean getVersion() { + return version; + } + + public void setVersion(Boolean version) { + this.version = version; + } + + public Boolean getHelp() { + return help; + } + + public void setHelp(Boolean help) { + this.help = help; + } + + public Boolean getDaemon() { + return daemon; + } + + public void setDaemon(Boolean daemon) { + this.daemon = daemon; + } + + public Boolean getOneOff() { + return oneOff; + } + + public void setOneOff(Boolean oneOff) { + this.oneOff = oneOff; + } + + public String getServerBitrateLimit() { + return serverBitrateLimit; + } + + public void setServerBitrateLimit(String serverBitrateLimit) { + this.serverBitrateLimit = serverBitrateLimit; + } + + public Integer getIdleTimeout() { + return idleTimeout; + } + + public void setIdleTimeout(Integer idleTimeout) { + this.idleTimeout = idleTimeout; + } + + public String getRsaPrivateKeyPath() { + return rsaPrivateKeyPath; + } + + public void setRsaPrivateKeyPath(String rsaPrivateKeyPath) { + this.rsaPrivateKeyPath = rsaPrivateKeyPath; + } + + public String getAuthorizedUsersPath() { + return authorizedUsersPath; + } + + public void setAuthorizedUsersPath(String authorizedUsersPath) { + this.authorizedUsersPath = authorizedUsersPath; + } + + public Integer getTimeSkewThreshold() { + return timeSkewThreshold; + } + + public void setTimeSkewThreshold(Integer timeSkewThreshold) { + this.timeSkewThreshold = timeSkewThreshold; + } + + public Boolean getUsePkcs1Padding() { + return usePkcs1Padding; + } + + public void setUsePkcs1Padding(Boolean usePkcs1Padding) { + this.usePkcs1Padding = usePkcs1Padding; + } + + public Boolean getSctp() { + return sctp; + } + + public void setSctp(Boolean sctp) { + this.sctp = sctp; + } + + public String getXbind() { + return xbind; + } + + public void setXbind(String xbind) { + this.xbind = xbind; + } + + public Integer getNstreams() { + return nstreams; + } + + public void setNstreams(Integer nstreams) { + this.nstreams = nstreams; + } + + + public Integer getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public String getPacingTimer() { + return pacingTimer; + } + + public void setPacingTimer(String pacingTimer) { + this.pacingTimer = pacingTimer; + } + + public String getFqRate() { + return fqRate; + } + + public void setFqRate(String fqRate) { + this.fqRate = fqRate; + } + + public Integer getTime() { + if(time == 0){ + time = 10; + } + return time; + } + + public void setTime(Integer time) { + this.time = time; + } + + public String getBytes() { + return bytes; + } + + public void setBytes(String bytes) { + this.bytes = bytes; + } + + public String getBlockcount() { + return blockcount; + } + + public void setBlockcount(String blockcount) { + this.blockcount = blockcount; + } + + public Integer getCport() { + return cport; + } + + public void setCport(Integer cport) { + this.cport = cport; + } + + public Integer getParallel() { + return parallel; + } + + public void setParallel(Integer parallel) { + this.parallel = parallel; + } + + public Boolean getReverse() { + return reverse; + } + + public void setReverse(Boolean reverse) { + this.reverse = reverse; + } + + public Boolean getBidir() { + return bidir; + } + + public void setBidir(Boolean bidir) { + this.bidir = bidir; + } + + public String getWindow() { + return window; + } + + public void setWindow(String window) { + this.window = window; + } + + public String getCongestion() { + return congestion; + } + + public void setCongestion(String congestion) { + this.congestion = congestion; + } + + public Integer getSetMss() { + return setMss; + } + + public void setSetMss(Integer setMss) { + this.setMss = setMss; + } + + public Boolean getNoDelay() { + return noDelay; + } + + public void setNoDelay(Boolean noDelay) { + this.noDelay = noDelay; + } + + public Boolean getVersion4() { + return version4; + } + + public void setVersion4(Boolean version4) { + this.version4 = version4; + } + + public Boolean getVersion6() { + return version6; + } + + public void setVersion6(Boolean version6) { + this.version6 = version6; + } + + public Integer getTos() { + return tos; + } + + public void setTos(Integer tos) { + this.tos = tos; + } + + public String getDscp() { + return dscp; + } + + public void setDscp(String dscp) { + this.dscp = dscp; + } + + public Integer getFlowlabel() { + return flowlabel; + } + + public void setFlowlabel(Integer flowlabel) { + this.flowlabel = flowlabel; + } + + public Boolean getZerocopy() { + return zerocopy; + } + + public void setZerocopy(Boolean zerocopy) { + this.zerocopy = zerocopy; + } + + public Integer getOmit() { + return omit; + } + + public void setOmit(Integer omit) { + this.omit = omit; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getExtraData() { + return extraData; + } + + public void setExtraData(String extraData) { + this.extraData = extraData; + } + + public Boolean getGetServerOutput() { + return getServerOutput; + } + + public void setGetServerOutput(Boolean getServerOutput) { + this.getServerOutput = getServerOutput; + } + + public Boolean getUdpCounters64bit() { + return udpCounters64bit; + } + + public void setUdpCounters64bit(Boolean udpCounters64bit) { + this.udpCounters64bit = udpCounters64bit; + } + + public Boolean getRepeatingPayload() { + return repeatingPayload; + } + + public void setRepeatingPayload(Boolean repeatingPayload) { + this.repeatingPayload = repeatingPayload; + } + + public Boolean getDontFragment() { + return dontFragment; + } + + public void setDontFragment(Boolean dontFragment) { + this.dontFragment = dontFragment; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getRsaPublicKeyPath() { + return rsaPublicKeyPath; + } + + public void setRsaPublicKeyPath(String rsaPublicKeyPath) { + this.rsaPublicKeyPath = rsaPublicKeyPath; + } + + @Override + public String toString() { + return "Params{" + + "iPerf3UUID='" + iPerf3UUID + '\'' + + ", protocol=" + protocol + + ", port=" + port + + ", interval=" + interval + + ", bitrate='" + bitrate + '\'' + + ", length=" + length + + ", mode=" + mode + + ", direction=" + direction + + ", pidfile='" + pidfile + '\'' + + ", file='" + file + '\'' + + ", affinity='" + affinity + '\'' + + ", bind='" + bind + '\'' + + ", bindDev='" + bindDev + '\'' + + ", verbose=" + verbose + + ", json=" + json + + ", jsonStream=" + jsonStream + + ", forceflush=" + forceflush + + ", timestamps='" + timestamps + '\'' + + ", rcvTimeout=" + rcvTimeout + + ", sndTimeout=" + sndTimeout + + ", debug=" + debug + + ", version=" + version + + ", help=" + help + + ", daemon=" + daemon + + ", oneOff=" + oneOff + + ", serverBitrateLimit='" + serverBitrateLimit + '\'' + + ", idleTimeout=" + idleTimeout + + ", rsaPrivateKeyPath='" + rsaPrivateKeyPath + '\'' + + ", authorizedUsersPath='" + authorizedUsersPath + '\'' + + ", timeSkewThreshold=" + timeSkewThreshold + + ", usePkcs1Padding=" + usePkcs1Padding + + ", sctp=" + sctp + + ", xbind='" + xbind + '\'' + + ", nstreams=" + nstreams + + ", connectTimeout=" + connectTimeout + + ", pacingTimer='" + pacingTimer + '\'' + + ", fqRate='" + fqRate + '\'' + + ", time=" + time + + ", bytes='" + bytes + '\'' + + ", blockcount='" + blockcount + '\'' + + ", cport=" + cport + + ", parallel=" + parallel + + ", reverse=" + reverse + + ", bidir=" + bidir + + ", window='" + window + '\'' + + ", congestion='" + congestion + '\'' + + ", setMss=" + setMss + + ", noDelay=" + noDelay + + ", version4=" + version4 + + ", version6=" + version6 + + ", tos=" + tos + + ", dscp='" + dscp + '\'' + + ", flowlabel=" + flowlabel + + ", zerocopy=" + zerocopy + + ", omit=" + omit + + ", title='" + title + '\'' + + ", extraData='" + extraData + '\'' + + ", getServerOutput=" + getServerOutput + + ", udpCounters64bit=" + udpCounters64bit + + ", repeatingPayload=" + repeatingPayload + + ", dontFragment=" + dontFragment + + ", username='" + username + '\'' + + ", rsaPublicKeyPath='" + rsaPublicKeyPath + '\'' + + '}'; + } + + public String[] getInputAsCommand() { + ArrayList command = new ArrayList<>(); + + // Mode: either CLIENT (-c host) or SERVER (-s) + switch (mode) { + case CLIENT: + command.add("-c"); + command.add(host); + break; + case SERVER: + command.add("-s"); + break; + default: + // Optionally, throw an exception or handle UNDEFINED mode + break; + } + + // Common options + if (port > 0) { + command.add("-p"); + command.add(String.valueOf(port)); + } + if (interval > 0) { + command.add("-i"); + command.add(String.valueOf(interval)); + } + if (pidfile != null && !pidfile.trim().isEmpty()) { + command.add("-I"); + command.add(pidfile); + } + if (file != null && !file.trim().isEmpty()) { + command.add("-F"); + command.add(file); + } + if (affinity != null && !affinity.trim().isEmpty()) { + command.add("-A"); + command.add(affinity); + } + if (bind != null && !bind.trim().isEmpty()) { + command.add("-B"); + command.add(bind); + } + if (bindDev != null && !bindDev.trim().isEmpty()) { + command.add("--bind-dev"); + command.add(bindDev); + } + if (verbose != null && verbose) { + command.add("-V"); + } + if (json != null && json) { + command.add("-J"); + } + + if (timestamps != null && !timestamps.trim().isEmpty()) { + command.add("--timestamps"); + command.add(timestamps); + } + if (rcvTimeout != null && rcvTimeout > 0) { + command.add("--rcv-timeout"); + command.add(String.valueOf(rcvTimeout)); + } + if (sndTimeout != null && sndTimeout > 0) { + command.add("--snd-timeout"); + command.add(String.valueOf(sndTimeout)); + } + if (debug != null && debug > 0) { + command.add("-d"); + command.add(String.valueOf(debug)); + } + if (version != null && version) { + command.add("-v"); + } + if (help != null && help) { + command.add("-h"); + } + + // Server-specific options + if (daemon != null && daemon) { + command.add("-D"); + } + if (oneOff != null && oneOff) { + command.add("-1"); + } + if (serverBitrateLimit != null && !serverBitrateLimit.trim().isEmpty()) { + command.add("--server-bitrate-limit"); + command.add(serverBitrateLimit); + } + if (idleTimeout != null && idleTimeout > 0) { + command.add("--idle-timeout"); + command.add(String.valueOf(idleTimeout)); + } + if (rsaPrivateKeyPath != null && !rsaPrivateKeyPath.trim().isEmpty()) { + command.add("--rsa-private-key-path"); + command.add(rsaPrivateKeyPath); + } + if (authorizedUsersPath != null && !authorizedUsersPath.trim().isEmpty()) { + command.add("--authorized-users-path"); + command.add(authorizedUsersPath); + } + if (timeSkewThreshold != null && timeSkewThreshold > 0) { + command.add("--time-skew-threshold"); + command.add(String.valueOf(timeSkewThreshold)); + } + if (usePkcs1Padding != null && usePkcs1Padding) { + command.add("--use-pkcs1-padding"); + } + + // Client-specific options + if (sctp != null && sctp) { + command.add("--sctp"); + } + if (xbind != null && !xbind.trim().isEmpty()) { + command.add("-X"); + command.add(xbind); + } + if (nstreams != null && nstreams > 0) { + command.add("--nstreams"); + command.add(String.valueOf(nstreams)); + } + switch (protocol){ + case UDP: + command.add("-u"); + break; + case TCP: + default: + break; + } + if (connectTimeout != null && connectTimeout > 0) { + command.add("--connect-timeout"); + command.add(String.valueOf(connectTimeout)); + } + if (bitrate != null && !bitrate.trim().isEmpty()) { + command.add("-b"); + command.add(bitrate+"M"); + } + if (pacingTimer != null && !pacingTimer.trim().isEmpty()) { + command.add("--pacing-timer"); + command.add(pacingTimer); + } + if (fqRate != null && !fqRate.trim().isEmpty()) { + command.add("--fq-rate"); + command.add(fqRate); + } + if (time > 0) { + command.add("-t"); + command.add(String.valueOf(time)); + } + if (bytes != null && !bytes.trim().isEmpty()) { + command.add("-n"); + command.add(bytes); + } + if (blockcount != null && !blockcount.trim().isEmpty()) { + command.add("-k"); + command.add(blockcount); + } + if (length > 0) { + command.add("-l"); + command.add(String.valueOf(length)); + } + if (cport != null && cport > 0) { + command.add("--cport"); + command.add(String.valueOf(cport)); + } + if (parallel != null && parallel > 0) { + command.add("-P"); + command.add(String.valueOf(parallel)); + } + + // Data direction options (client-specific) + if (direction != null) { + switch (direction) { + case DOWN: + command.add("--reverse"); + break; + case BIDIR: + command.add("--bidir"); + break; + default: + // UP direction requires no flag. + break; + } + } + + if (window != null && !window.trim().isEmpty()) { + command.add("-w"); + command.add(window); + } + if (congestion != null && !congestion.trim().isEmpty()) { + command.add("-C"); + command.add(congestion); + } + if (setMss != null && setMss > 0) { + command.add("-M"); + command.add(String.valueOf(setMss)); + } + if (noDelay != null && noDelay) { + command.add("-N"); + } + if (version4 != null && version4) { + command.add("-4"); + } + if (version6 != null && version6) { + command.add("-6"); + } + if (tos != null && tos > 0) { + command.add("-S"); + command.add(String.valueOf(tos)); + } + if (dscp != null && !dscp.trim().isEmpty()) { + command.add("--dscp"); + command.add(dscp); + } + if (flowlabel != null && flowlabel > 0) { + command.add("-L"); + command.add(String.valueOf(flowlabel)); + } + if (zerocopy != null && zerocopy) { + command.add("-Z"); + } + if (omit != null && omit > 0) { + command.add("-O"); + command.add(String.valueOf(omit)); + } + if (title != null && !title.trim().isEmpty()) { + command.add("-T"); + command.add(title); + } + if (extraData != null && !extraData.trim().isEmpty()) { + command.add("--extra-data"); + command.add(extraData); + } + if (getServerOutput != null && getServerOutput) { + command.add("--get-server-output"); + } + if (udpCounters64bit != null && udpCounters64bit) { + command.add("--udp-counters-64bit"); + } + if (repeatingPayload != null && repeatingPayload) { + command.add("--repeating-payload"); + } + if (dontFragment != null && dontFragment) { + command.add("--dont-fragment"); + } + if (username != null && !username.trim().isEmpty()) { + command.add("--username"); + command.add(username); + } + if (rsaPublicKeyPath != null && !rsaPublicKeyPath.trim().isEmpty()) { + command.add("--rsa-public-key-path"); + command.add(rsaPublicKeyPath); + } + + // Always add these extra fixed options. + command.add("--json-stream"); + command.add("--forceflush"); + command.add("--logfile"); + command.add(super.getLogfile()); + + + + return command.toArray(new String[0]); + } + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Parameter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Parameter.java new file mode 100644 index 00000000..d4d8f6d0 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/Parameter.java @@ -0,0 +1,56 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter; + +import android.os.Environment; +import android.os.Parcel; +import android.os.Parcelable; + +public class Parameter implements Parcelable { + public static final String rootPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(); + private String lineProtocolFile; + private String logfile; + protected Parameter(Parcel in) { + lineProtocolFile = in.readString(); + logfile = in.readString(); + } + + public Parameter(String logfile, String lineProtocolFile) { + this.logfile = logfile; + this.lineProtocolFile = lineProtocolFile; + } + + public static final Creator CREATOR = new Creator() { + @Override + public Parameter createFromParcel(Parcel in) { + return new Parameter(in); + } + + @Override + public Parameter[] newArray(int size) { + return new Parameter[size]; + } + }; + + public String getLineProtocolFile() { + return lineProtocolFile; + } + + public String getLogfile() { + return logfile; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(android.os.Parcel dest, int flags) { + dest.writeString(lineProtocolFile); + dest.writeString(logfile); + } + public void setLineProtocolFile(String lineProtocolFile) { + this.lineProtocolFile = lineProtocolFile; + } + + public void setLogfile(String logfile) { + this.logfile = logfile; + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/PingParameter.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/PingParameter.java new file mode 100644 index 00000000..74b2137c --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Parameter/PingParameter.java @@ -0,0 +1,182 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter; + +import android.net.Network; +import android.os.Parcel; +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; + +public class PingParameter extends Parameter { + public static final String rawDirPath = rootPath+"/omnt/ping/raw/"; + + public static final String lineProtocolDirPath = rootPath+"/omnt/ping/lineprotocol/"; + private static final String TAG = "PingParameter"; + + public static final String PING = "ping"; + public static final String DESTINATION = "destination"; + public static final String COUNT = "count"; + public static final String TIMEOUT = "timeout"; // in seconds + public static final String PACKET_SIZE = "packetSize"; + public static final String INTERVAL = "interval"; // in seconds + + public String getDestination() { + return destination; + } + + public int getCount() { + return count; + } + + public int getTimeoutMillis() { + return timeoutMillis; + } + + public int getPacketSize() { + return packetSize; + } + + public long getIntervalMillis() { + return intervalMillis; + } + + + public Network getNetwork() { + return network; + } + + private String destination; + private int count; + private int timeoutMillis; + private int packetSize; + private long intervalMillis; + private Network network; + private int deadline; + private String testUUID; + + + public String[] getInputAsCommand() { + ArrayList command = new ArrayList<>(); + command.add("/system/bin/ping"); + if (count > 0) { + command.add("-c"); + command.add(String.valueOf(count)); + } + if (timeoutMillis > 0) { + command.add("-W"); + command.add(String.valueOf(timeoutMillis)); + } + if (packetSize > 0) { + command.add("-s"); + command.add(String.valueOf(packetSize)); + } + if (intervalMillis > 0) { + command.add("-i"); + command.add(String.valueOf(intervalMillis)); + } + if (deadline > 0) { + command.add("-w"); + command.add(String.valueOf(deadline)); + } + command.add("-D"); + command.add(destination); + + return command.toArray(new String[0]); + } + public PingParameter(JSONObject parameter, String testUUID) { + super(rawDirPath + testUUID + ".txt", lineProtocolDirPath + testUUID + ".txt"); + this.testUUID = testUUID; + try { + destination = parameter.getString(DESTINATION); + } catch (JSONException e) { + Log.d(TAG, e.toString()); + Log.w(TAG, "could not create PingParameter!"); + throw new IllegalArgumentException("Destination is missing"); + } + try { + count = parameter.getInt(COUNT); + } catch (JSONException e) { + Log.d(TAG, e.toString()); + Log.i(TAG, "No count set."); + } + try { + timeoutMillis = parameter.getInt(TIMEOUT); + } catch (JSONException e) { + Log.d(TAG, e.toString()); + Log.i(TAG, "no timeout set."); + } + try { + packetSize = parameter.getInt(PACKET_SIZE); + } catch (JSONException e) { + Log.d(TAG, e.toString()); + Log.i(TAG, "no packet size set."); + } + try { + intervalMillis = parameter.getLong(INTERVAL); + } catch (JSONException e) { + Log.d(TAG, e.toString()); + Log.i(TAG, "no interval set."); + } + try { + deadline = parameter.getInt("deadline"); + } catch (JSONException e) { + Log.d(TAG, e.toString()); + Log.i(TAG, "no deadline set."); + } + + try { + Files.createDirectories(Paths.get(rawDirPath)); + Files.createDirectories(Paths.get(lineProtocolDirPath)); + } catch (IOException e) { + Log.d(TAG, "Could not create directories."); + } + + + } + + protected PingParameter(Parcel in) { + super(in); + destination = in.readString(); + count = in.readInt(); + timeoutMillis = in.readInt(); + packetSize = in.readInt(); + intervalMillis = in.readLong(); + network = in.readParcelable(Network.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PingParameter createFromParcel(Parcel in) { + return new PingParameter(in); + } + + @Override + public PingParameter[] newArray(int size) { + return new PingParameter[size]; + } + }; + + @Override + public int describeContents() { + return super.describeContents(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(destination); + dest.writeInt(count); + dest.writeInt(timeoutMillis); + dest.writeInt(packetSize); + dest.writeLong(intervalMillis); + dest.writeParcelable(network, flags); + } + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingFragment.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingFragment.java index 0947055d..7175aeb7 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingFragment.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingFragment.java @@ -32,10 +32,8 @@ import java.io.FileOutputStream; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.METRIC_TYPE; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.Metric; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.PacketLossLine; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.PingInformation; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.RTTLine; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricCalculator; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Metric.MetricView; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; @@ -51,8 +49,8 @@ public class PingFragment extends Fragment { private TextInputEditText input; private Context ct; private SharedPreferencesGrouper spg; - private Metric rttMetric; - private Metric packetLossMetric; + private MetricView rttMetric; + private MetricView packetLossMetric; public PingFragment() { } @@ -67,8 +65,8 @@ private void startPingService() { input.setEnabled(false); Intent pingStart = new Intent(ct, PingService.class); ct.startService(pingStart); - rttMetric.resetMetric(); - packetLossMetric.resetMetric(); + rttMetric.getMetricCalculator().resetMetric(); + packetLossMetric.getMetricCalculator().resetMetric(); } private void stopPingService() { @@ -160,8 +158,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, }); - rttMetric = new Metric(METRIC_TYPE.PING_RTT, ct); - packetLossMetric = new Metric(METRIC_TYPE.PING_PACKET_LOSS, ct); + rttMetric = new MetricView(new MetricCalculator(METRIC_TYPE.PING_RTT), ct); + rttMetric.setup("RTT [ms]"); + packetLossMetric = new MetricView(new MetricCalculator(METRIC_TYPE.PACKET_LOSS), ct); + packetLossMetric.setup("Packet Loss [%]"); LinearLayout metricsLL = new LinearLayout(ct); metricsLL.setOrientation(LinearLayout.VERTICAL); LinearLayout.LayoutParams foo1 = new LinearLayout.LayoutParams( @@ -169,13 +169,14 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, LinearLayout.LayoutParams.WRAP_CONTENT ); metricsLL.setLayoutParams(foo1); - metricsLL.addView(rttMetric.createMainLL("RTT [ms]")); - metricsLL.addView(packetLossMetric.createMainLL("Packet Loss [%]")); + metricsLL.addView(rttMetric); + metricsLL.addView(packetLossMetric); horizontalLL1.addView(metricsLL); - - PingParser pingParser = PingParser.getInstance(null); + //ToDO + /* + PingParser pingParser = PingParser; pingParser.addPropertyChangeListener(evt -> { PingInformation pi = (PingInformation) evt.getNewValue(); switch (pi.getLineType()) { @@ -188,7 +189,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, break; } }); - + */ //packetLossMetric.setVisibility(View.INVISIBLE); return v; } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingInformations/PacketLossLine.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingInformations/PacketLossLine.java index 33bbc2d7..9f31057d 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingInformations/PacketLossLine.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingInformations/PacketLossLine.java @@ -1,5 +1,6 @@ package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations; +import com.influxdb.client.domain.WritePrecision; import com.influxdb.client.write.Point; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -15,12 +16,14 @@ public PacketLossLine(String line) { this.setLineType(LINEType.PACKET_LOSS); } public void parse(){ + super.parse(); matcher = pattern.matcher(this.getLine()); if(matcher.find()){ packetsTransmitted = Long.parseLong(matcher.group(1)); packetsReceived = Long.parseLong(matcher.group(2)); packetLoss = Double.parseDouble(matcher.group(3).replace("%", "")); } + } public long getPacketsReceived() { return packetsReceived; @@ -36,6 +39,8 @@ public Point getPoint(){ return super.getPoint() .addField("packets_transmitted", this.getPacketsTransmitted()) .addField("packets_received", this.getPacketsReceived()) - .addField("packet_loss", this.getPacketLoss()); + .addField("packet_loss", this.getPacketLoss()) + .time(System.currentTimeMillis(), WritePrecision.MS); + //ping does not provide timestamp for packet loss line } } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingParser.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingParser.java index 78fc4fa4..c0f40c99 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingParser.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingParser.java @@ -18,17 +18,13 @@ public class PingParser { private final PropertyChangeSupport support; private PropertyChangeListener listener; - private PingParser(BufferedReader br) { + public PingParser(BufferedReader br) { this.br = br; this.lines = new ArrayList<>(); support = new PropertyChangeSupport(this); } - public static PingParser getInstance(BufferedReader br){ - if (instance == null) instance = new PingParser(br); - if(br != null); instance.setBr(br); - return instance; - } + private LINEType getLineType(String line){ if (line.contains("bytes from")) { return LINEType.RTT; diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java index 8f9f1f6e..f0fcaf70 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingService.java @@ -133,11 +133,6 @@ private void setupPing(){ pingLoggingHandleThread = new HandlerThread("PingLoggingHandlerThread"); pingLoggingHandleThread.start(); pingLogging = new Handler(Objects.requireNonNull(pingLoggingHandleThread.getLooper())); - PingParser pingParser = PingParser.getInstance(null); - propertyChangeListener = pingParser.getListener(); - if(propertyChangeListener != null){ - pingParser.removePropertyChangeListener(propertyChangeListener); - } propertyChangeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { @@ -163,8 +158,6 @@ public void propertyChange(PropertyChangeEvent evt) { } }; - pingParser.addPropertyChangeListener(propertyChangeListener); - pingParser.setListener(propertyChangeListener); pingLogging.post(pingUpdate); } public void parsePingCommand() { @@ -196,38 +189,9 @@ public void run() { Data data = new Data.Builder() .putString("input", spg.getSharedPreference(SPType.ping_sp).getString("ping_input", "8.8.8.8")) .build(); - OneTimeWorkRequest pingWR = - new OneTimeWorkRequest.Builder(PingWorker.class) - .setInputData(data) - .addTag("Ping").build(); - pingWRs.add(pingWR); - wm.beginWith(pingWR).enqueue(); - Observer observer = new Observer() { - @Override - public void onChanged(Object o) { - WorkInfo workInfo = (WorkInfo) o; - WorkInfo.State state = workInfo.getState(); - switch (state){ - case RUNNING: - case ENQUEUED: - return; - case CANCELLED: - try { - ping_stream.close(); - } catch (IOException e) { - Log.d(TAG,e.toString()); - } - pingLogging.removeCallbacks(pingUpdate); - return; - } - wm.getWorkInfoByIdLiveData(pingWR.getId()).removeObserver(this); - pingWRs.remove(pingWR); - pingLogging.postDelayed(pingUpdate, 200); - } - }; - wm.getWorkInfoByIdLiveData(pingWR.getId()).observeForever(observer); + } }; diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingWorker.java deleted file mode 100644 index dd1d04d7..00000000 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/PingWorker.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Peter Hasse - * SPDX-FileCopyrightText: 2023 Johann Hackler - * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS - * - * SPDX-License-Identifier: BSD-3-Clause-Clear - */ - -package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping; - -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.graphics.Color; -import android.os.Bundle; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.core.app.NotificationCompat; -import androidx.work.Data; -import androidx.work.ForegroundInfo; -import androidx.work.WorkManager; -import androidx.work.Worker; -import androidx.work.WorkerParameters; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; - -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.MainActivity; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.PingInformation; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.RTTLine; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; -import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; - - -public class PingWorker extends Worker { - - private static final String TAG = "PingWorker"; - Runtime runtime; - private ArrayList lines; - private Process pingProcess; - private final int notificationID = 102; - HashMap parsedCommand = new HashMap<>(); - private String pingCommand; - private final int FOREGROUND_SERVICE_TYPE = FOREGROUND_SERVICE_TYPE_SPECIAL_USE; - private final Context ct; - private final String channelId = "OMNT_notification_channel"; - private NotificationCompat.Builder notificationBuilder; - private Notification notification; - private final String timeRegex = "\\btime=([0-9]+\\.[0-9]+)\\s+ms\\b"; - private double rtt; - private NotificationManager notificationManager; - private SharedPreferencesGrouper spg; - - - public void parsePingCommand() { - - String[] commandParts = pingCommand.split("\\s+"); - - String previousPart = null; - for (String part : commandParts) { - switch (part) { - case "ping": - parsedCommand.put("command", part); - break; - case "-w": - if (previousPart != null) { - parsedCommand.put("timeout", previousPart); - } - break; - default: - parsedCommand.put("target", part); - break; - } - previousPart = part; - } - } - - public PingWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - runtime = Runtime.getRuntime(); - ct = context; - spg = SharedPreferencesGrouper.getInstance(ct); - } - - - @Override - public void onStopped() { - super.onStopped(); - Log.d(TAG, "onStopped: worker stopped!"); - if(pingProcess.isAlive()) pingProcess.destroy(); - spg.getSharedPreference(SPType.ping_sp).edit().putBoolean("ping_running", false).apply(); - - } - - private ForegroundInfo createForegroundInfo(@NonNull String progress) { - // Create an Intent to launch the main activity - Intent launchIntent = new Intent(ct, MainActivity.class); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - - // Add a Bundle to the Intent to indicate that the PingFragment should be opened - Bundle bundle = new Bundle(); - bundle.putString("openFragment", "PingFragment"); - launchIntent.putExtras(bundle); - - // Create a PendingIntent with the Intent - PendingIntent pendingIntent = PendingIntent.getActivity(ct, 0, launchIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - - PendingIntent cancelIntent = WorkManager.getInstance(ct) - .createCancelPendingIntent(getId()); - - notification = notificationBuilder - .setContentTitle("Ping " + parsedCommand.get("target")) - .setContentText(progress) - .setOngoing(true) - .setOnlyAlertOnce(true) - .setColor(Color.WHITE) - .setSmallIcon(R.mipmap.ic_launcher_foreground) - .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_DEFAULT) - .addAction(R.drawable.ic_close, "Cancel", cancelIntent) - .setContentIntent(pendingIntent) // Set the content intent - .build(); - return new ForegroundInfo(notificationID, notification, FOREGROUND_SERVICE_TYPE); - } - - Runnable updateNotification = new Runnable() { - @Override - public void run() { - if(notification != null){ - notificationBuilder.setContentText(rtt+" ms"); - notificationManager.notify(notificationID, notificationBuilder.build()); - } else { - setForegroundAsync(createForegroundInfo(rtt+" ms")); - } - } - }; - - @NonNull - @Override - public Result doWork() { - lines = new ArrayList<>(); - Data data = null; - notificationBuilder = new NotificationCompat.Builder(ct, channelId); - notificationManager = ct.getSystemService(NotificationManager.class); - try { - - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("/system/bin/ping ") - .append("-D ") - .append(getInputData().getString("input")); - - pingCommand = stringBuilder.toString(); - parsePingCommand(); - pingProcess = runtime.exec(stringBuilder.toString()); - - BufferedReader outputReader = - new BufferedReader(new InputStreamReader(pingProcess.getInputStream())); - - - - PingParser pingParser = PingParser.getInstance(outputReader); - - pingParser.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - PingInformation pi = (PingInformation) evt.getNewValue(); - switch(pi.getLineType()){ - case PACKET_LOSS: - break; - case RTT: - rtt = ((RTTLine)pi).getRtt(); - break; - } - updateNotification.run(); - } - }); - pingParser.parse(); - int result = pingProcess.waitFor(); - - if(isStopped()){ - Log.d(TAG, "doWork: got cancelled because Worker got stopped!"); - - return Result.success(); - } - - - Log.d(TAG, "doWork: result " + result); - if (result != 0) { - return Result.failure(); - } - } catch (IOException e) { - Log.d(TAG,e.toString()); - System.out.printf(e.toString()); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - return Result.success(); - } -} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingToLineProtocolWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingToLineProtocolWorker.java new file mode 100644 index 00000000..faad5762 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingToLineProtocolWorker.java @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.Worker; + +import android.content.Context; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.Data; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.google.gson.Gson; +import com.influxdb.client.write.Point; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Scanner; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.DataProvider.DeviceInformation; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.GlobalVars; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.InfluxDB2x.InfluxdbConnection; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.PingInput; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.LINEType; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.PacketLossLine; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.PingInformation; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.PingInformations.RTTLine; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; + +public class PingToLineProtocolWorker extends Worker { + public static final String TAG = "PingToLineProtocolWorker"; + InfluxdbConnection influx; + private SharedPreferencesGrouper spg; + + private final DeviceInformation di = GlobalVars.getInstance().get_dp().getDeviceInformation(); + private PingInput pingInput; + public PingToLineProtocolWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + Gson gson = new Gson(); + String iperf3InputString = getInputData().getString(PingInput.INPUT); + pingInput = gson.fromJson(iperf3InputString, PingInput.class); + spg = SharedPreferencesGrouper.getInstance(getApplicationContext()); + } + + + private LINEType getLineType(String line){ + if (line.contains("bytes from")) { + return LINEType.RTT; + } else if (line.contains("Unreachable")) { + return LINEType.UNREACHABLE; + } else if (line.contains("Request timeout")) { + return LINEType.TIMEOUT; + } else if (line.contains("packets transmitted")){ + return LINEType.PACKET_LOSS; + } else { + return LINEType.UNKNOWN; + } + } + + @NonNull + @Override + public Result doWork() { + Data.Builder output = new Data.Builder().putBoolean("pingUpload", false); + File myObj = new File(pingInput.getPingParameter().getLogfile()); + Scanner scanner = null; + try { + scanner = new Scanner(myObj); + } catch (FileNotFoundException e) { + return Result.failure(output.putString("error", "File not found").build()); + } + ArrayList pingInformations = new ArrayList<>(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + PingInformation pi = null; + switch (getLineType(line)) { + case RTT: + pi = new RTTLine(line); + case UNREACHABLE: + //TDODO + break; + case TIMEOUT: + //TODO + break; + case PACKET_LOSS: + pi = new PacketLossLine(line); + break; + case UNKNOWN: + break; + } + if (pi == null) continue; + pi.parse(); + pingInformations.add(pi); + } + scanner.close(); + File lineprotocolfile = new File(pingInput.getPingParameter().getLineProtocolFile()); + if(lineprotocolfile.exists()){ + lineprotocolfile.delete(); + try { + lineprotocolfile.createNewFile(); + } catch (IOException e) { + Log.e(TAG, "doWork: could not create LP File!", e); + return Result.failure(output.putString("error", "LP-File not created").build()); + } + } + FileOutputStream pingStream = null; + try { + pingStream = new FileOutputStream(pingInput.getPingParameter().getLineProtocolFile(), true); + } catch (FileNotFoundException e) { + Log.d(TAG, "doWork: " + e.toString()); + Log.e(TAG, "doWork: Could not create FileOutputStream"); + } + + for (PingInformation pi : pingInformations) { + try { + Point point = pi.getPoint(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + point.addTags(GlobalVars.getInstance().get_dp().getTagsMap()); + } + pingStream.write((point.toLineProtocol() + "\n").getBytes()); + + } catch (IOException e) { + Log.d(TAG, "doWork: "+e.toString()); + return Result.failure(output.putString("error", "File not written").build()); + } + } + return Result.success(output.build()); + } + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingWorker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingWorker.java new file mode 100644 index 00000000..1f9e30de --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Ping/Worker/PingWorker.java @@ -0,0 +1,165 @@ +/* + * SPDX-FileCopyrightText: 2023 Peter Hasse + * SPDX-FileCopyrightText: 2023 Johann Hackler + * SPDX-FileCopyrightText: 2023 Fraunhofer FOKUS + * + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.Ping.Worker; + +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.graphics.Color; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.work.Data; +import androidx.work.ForegroundInfo; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.google.gson.Gson; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Inputs.PingInput; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Parameter.PingParameter; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; + +public class PingWorker extends Worker { + + public static final String PING = "ping"; + public static final String REASON = "reason"; + public static final String LINE = "line"; + public static final String TAG = "PingWorker"; + + private int notificationID = 102; + private final int FOREGROUND_SERVICE_TYPE = FOREGROUND_SERVICE_TYPE_SPECIAL_USE; + private final Context ct; + private NotificationManager notificationManager; + private final String channelId = "OMNT_notification_channel"; + private NotificationCompat.Builder notificationBuilder; + private float rtt; // round-trip time + private PingInput pingInput; + private Notification notification; + public PingWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + ct = context; + + // Retrieve the PingInput from the Worker's input data. + String pingInputString = getInputData().getString(PingInput.INPUT); + pingInput = new Gson().fromJson(pingInputString, PingInput.class); + + int notificationNumber = getInputData().getInt(PingInput.NOTIFICATIONUMBER, 0); + notificationID += notificationNumber; + + notificationManager = (NotificationManager) ct.getSystemService(Context.NOTIFICATION_SERVICE); + notificationBuilder = new NotificationCompat.Builder(ct, channelId); + setForegroundAsync(createForegroundInfo("")); + } + + private ForegroundInfo createForegroundInfo(String progress) { + notification = notificationBuilder + .setContentTitle("Ping") + .setContentText(pingInput.getPingParameter().getDestination()+": "+progress) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setColor(Color.WHITE) + .setSmallIcon(R.mipmap.ic_launcher_foreground) + .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_DEFAULT) + .build(); + return new ForegroundInfo(notificationID, notification, FOREGROUND_SERVICE_TYPE); + } + + + @NonNull + @Override + public Result doWork() { + Data.Builder output = new Data.Builder().putBoolean(PING, false); + + if (pingInput == null) { + Log.e(TAG, "PingInput is null"); + return Result.failure(output.putString(REASON, "PingInput is null").build()); + } + PingParameter pingParameter = pingInput.getPingParameter(); + if (pingParameter == null) { + Log.e(TAG, "PingParameter is null"); + return Result.failure(output.putString(REASON, "PingParameter is null").build()); + } + String[] command = pingParameter.getInputAsCommand(); + if (command == null) { + Log.e(TAG, "Command is null"); + return Result.failure(output.putString(REASON, "Command is null").build()); + } + + Log.d(TAG, "doWork: executing " + String.join(" ", command)); + int result = -1; + try { + String timeRegex = "time=(\\d+(?:\\.\\d+)?)\\s*ms"; + Pattern pattern = Pattern.compile(timeRegex); + + + ProcessBuilder processBuilder = new ProcessBuilder(command); + Process process = processBuilder.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + FileOutputStream pingStream = new FileOutputStream(pingInput.getPingParameter().getLogfile(), true); + + String line; + while ((line = reader.readLine()) != null) { + Log.d(TAG, "doWork: "+line); + pingStream.write((line + "\n").getBytes()); + Matcher matcher = pattern.matcher(line); + if (matcher.find()) { + try { + rtt = Float.parseFloat(Objects.requireNonNull(matcher.group(1))); + Log.d(TAG, "Updated RTT: " + rtt); + new Runnable() { + @Override + public void run() { + setForegroundAsync(createForegroundInfo( rtt + " ms")); + } + }.run(); + } catch (NumberFormatException e) { + Log.e(TAG, "Error parsing RTT value: " + e.toString()); + } + } + // Optionally, report progress with the output line. + setProgressAsync(output.putString(LINE, line).build()); + } + + pingStream.close(); + reader.close(); + + result = process.waitFor(); + } catch (IOException e) { + Log.e(TAG, "Error while executing ping command: " + e.toString()); + return Result.failure(output.putString(REASON, "Error while executing ping command.").build()); + } catch (InterruptedException e) { + Log.e(TAG, "Error while waiting for ping command: " + e.toString()); + return Result.failure(output.putString(REASON, "Error while waiting for ping command.").build()); + } + + if (result != 0) { + Log.e(TAG, "Ping command failed with result: " + result); + return Result.failure(output.putString(REASON, "Ping command failed with result: " + result).build()); + } + + return Result.success(output.putBoolean(PING, true).build()); + } +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SPType.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SPType.java index 6607b0ab..893fc16c 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SPType.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SPType.java @@ -6,7 +6,8 @@ public enum SPType { ping_sp, carrier_sp, mobile_network_sp, - default_sp; + default_sp, + mqtt_sp; public String toString() { @@ -16,6 +17,7 @@ public String toString() { case logging_sp: case ping_sp: case carrier_sp: + case mqtt_sp: case mobile_network_sp: return super.toString(); default: @@ -37,12 +39,15 @@ public String toReadable(){ case logging_sp: case ping_sp: case carrier_sp: + case default_sp: return name.substring(0,1).toUpperCase() + name.substring(1); case mobile_network_sp: return "Mobile Network"; case iperf3_sp: return "iPerf3"; + case mqtt_sp: + return "MQTT"; default: return null; } diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SharedPreferencesGrouper.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SharedPreferencesGrouper.java index 26c35a4f..2782ace0 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SharedPreferencesGrouper.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/Preferences/SharedPreferencesGrouper.java @@ -21,6 +21,7 @@ public class SharedPreferencesGrouper { private final SharedPreferences carrierSP; private final SharedPreferences iperf3SP; private final SharedPreferences mobileNetworkSP; + private final SharedPreferences mqttSP; private final SharedPreferences defaultSP; private final SharedPreferences pingSP; private final Context ct; @@ -36,12 +37,13 @@ public void clearConfig(){ } - private SharedPreferencesGrouper(Context ct) { + private SharedPreferencesGrouper( Context ct) { this.ct = ct; loggingSP = ct.getSharedPreferences(getSharedPreferenceIdentifier(SPType.logging_sp), Context.MODE_PRIVATE); carrierSP = ct.getSharedPreferences(getSharedPreferenceIdentifier(SPType.carrier_sp), Context.MODE_PRIVATE); iperf3SP = ct.getSharedPreferences(getSharedPreferenceIdentifier(SPType.iperf3_sp), Context.MODE_PRIVATE); pingSP = ct.getSharedPreferences(getSharedPreferenceIdentifier(SPType.ping_sp), Context.MODE_PRIVATE); + mqttSP = ct.getSharedPreferences(getSharedPreferenceIdentifier(SPType.mqtt_sp), Context.MODE_PRIVATE); mobileNetworkSP = ct.getSharedPreferences(getSharedPreferenceIdentifier(SPType.mobile_network_sp), Context.MODE_PRIVATE); defaultSP = PreferenceManager.getDefaultSharedPreferences(ct); } @@ -71,6 +73,9 @@ public SharedPreferences getSharedPreference(SPType key){ case mobile_network_sp: sp = mobileNetworkSP; break; + case mqtt_sp: + sp = mqttSP; + break; default: sp = defaultSP; break; diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/RemoteWorkInfoChecker.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/RemoteWorkInfoChecker.java new file mode 100644 index 00000000..91333b22 --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/RemoteWorkInfoChecker.java @@ -0,0 +1,97 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit; + +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.WorkInfo; +import androidx.work.WorkQuery; +import androidx.work.multiprocess.RemoteWorkManager; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Executor; + + +public class RemoteWorkInfoChecker implements Runnable { + private static final String TAG = "WorkInfoChecker"; + private final Handler handler = new Handler(Looper.getMainLooper()); + private final HashMap workInfors = new HashMap<>(); + private final RemoteWorkManager remoteWorkManager; + private final ArrayList workIdGroups; + private boolean isDone = false; + private final Executor executor; + private CustomEventListener listener; + + private int workCount = 0; + public RemoteWorkInfoChecker(RemoteWorkManager remoteWorkManager, ArrayList workIdGroups) { + this.remoteWorkManager = remoteWorkManager; + this.workIdGroups = workIdGroups; + this.executor = Runnable::run; + } + + + private void update(WorkInfo workInfo) { + workInfors.put(workInfo.getId(), workInfo); + if(listener != null) { + listener.onChange(new HashMap<>(workInfors)); + } + } + + private void checkWorkInfos(UUID workIds) { + ListenableFuture> future = remoteWorkManager.getWorkInfos(WorkQuery.fromIds(workIds)); + Futures.addCallback(future, new com.google.common.util.concurrent.FutureCallback>() { + @Override + public void onSuccess(List workInfos) { + Log.d(TAG, "onSuccess: WorkInfos: " + workInfos.size()); + for (WorkInfo workInfo : workInfos) { + Log.d(TAG, "onSuccess: WorkInfo: " + workInfo.getId() + " State: " + workInfo.getState()); + update(workInfo); + } + } + + @Override + public void onFailure(@NonNull Throwable throwable) { + Log.e(TAG, " executeWork: Error getting work info: " + throwable.getMessage()); + } + }, executor); + } + + private void isAllWorkDone() { + for (WorkInfo info : workInfors.values()) { + WorkInfo.State state = info.getState(); + isDone = (state != WorkInfo.State.RUNNING) && (state != WorkInfo.State.ENQUEUED); + } + } + + @Override + public void run() { + for (int i = 0; i < workIdGroups.size(); i++) { + checkWorkInfos(workIdGroups.get(i)); + } + + executor.execute(this::isAllWorkDone); + + if (isDone || workIdGroups.isEmpty()) { + Log.d(TAG, "run: All work done"); + handler.removeCallbacks(this); + } else { + handler.postDelayed(this, 1000); + } + } + + public void setListener(CustomEventListener listener) { + this.listener = listener; + } + + public void start() { + handler.postDelayed(this, 200); + } + +} diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/LoggingSettingsFragment.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/LoggingSettingsFragment.java index 0be64e35..3ddfab74 100644 --- a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/LoggingSettingsFragment.java +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/LoggingSettingsFragment.java @@ -13,10 +13,14 @@ import android.text.InputType; import android.util.Log; +import androidx.fragment.app.FragmentTransaction; +import androidx.preference.EditTextPreference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreferenceCompat; +import java.util.Objects; + import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; @@ -31,25 +35,77 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { SharedPreferencesGrouper spg = SharedPreferencesGrouper.getInstance(requireContext()); getPreferenceManager().setSharedPreferencesName(spg.getSharedPreferenceIdentifier(SPType.logging_sp)); setPreferencesFromResource(R.xml.preference_logging, rootKey); - getPreferenceScreen().getSharedPreferences() + Objects.requireNonNull(getPreferenceScreen().getSharedPreferences()) .registerOnSharedPreferenceChangeListener(this); - enable_influx_switch = findPreference("enable_influx"); - - androidx.preference.EditTextPreference editTextPreference = getPreferenceManager().findPreference("logging_interval"); editTextPreference.setOnBindEditTextListener( editText -> editText.setInputType(InputType.TYPE_CLASS_NUMBER)); } + @Override + public void onResume() { + super.onResume(); + // Register the listener + Objects.requireNonNull(getPreferenceManager().getSharedPreferences()) + .registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onPause() { + super.onPause(); + // Unregister the listener to prevent memory leaks + Objects.requireNonNull(getPreferenceManager().getSharedPreferences()) + .unregisterOnSharedPreferenceChangeListener(this); + } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { if(s == null) return; + Log.d(TAG, "onSharedPreferenceChanged: " + s); if (s.equals("enable_logging")) { boolean logger = sharedPreferences.getBoolean("enable_logging", false); - Log.d(TAG, "Logger update: " + logger); + Log.d(TAG, "onSharedPreferenceChanged: " + logger); + } + + switch (s){ + case "enable_logging": + case "start_logging_on_boot": + case "enable_local_influx_log": + case "enable_local_file_log": + case "enable_influx": + case "fake_location": + case "influx_network_data": + case "log_signal_data": + case "influx_cell_data": + case "log_neighbour_cells": + case "influx_throughput_data": + case "log_wifi_data": + case "influx_battery_data": + case "influx_ip_address_data": + boolean booleanValue = sharedPreferences.getBoolean(s, false); + SwitchPreferenceCompat switchPreferenceCompat = findPreference(s); + if (switchPreferenceCompat != null) { + switchPreferenceCompat.setChecked(booleanValue); + } + break; + case "logging_interval": + case "influx_URL": + case "influx_token": + case "influx_org": + case "influx_bucket": + case "measurement_name": + case "tags": + String stringValue = sharedPreferences.getString(s, ""); + EditTextPreference editTextPreference = findPreference(s); + if (editTextPreference != null) { + editTextPreference.setText(stringValue); + } + + break; + } + } @Override diff --git a/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/MQTTSettingsFragment.java b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/MQTTSettingsFragment.java new file mode 100644 index 00000000..b65c902c --- /dev/null +++ b/app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/SettingPreferences/MQTTSettingsFragment.java @@ -0,0 +1,44 @@ +package de.fraunhofer.fokus.OpenMobileNetworkToolkit.SettingPreferences; + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.text.InputType; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.SwitchPreferenceCompat; + +import java.util.Objects; + +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SPType; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.Preferences.SharedPreferencesGrouper; +import de.fraunhofer.fokus.OpenMobileNetworkToolkit.R; + +public class MQTTSettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { + private static final String TAG = "MQTTSettingsFragment"; + SwitchPreferenceCompat enable_mqtt_switch; + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) { + SharedPreferencesGrouper spg = SharedPreferencesGrouper.getInstance(requireContext()); + getPreferenceManager().setSharedPreferencesName(spg.getSharedPreferenceIdentifier(SPType.mqtt_sp)); + setPreferencesFromResource(R.xml.preference_mqtt, rootKey); + Objects.requireNonNull(getPreferenceScreen().getSharedPreferences()) + .registerOnSharedPreferenceChangeListener(this); + enable_mqtt_switch = findPreference("enable_mqtt"); + + + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) { + if(key == null) return; + if (key.equals("enable_mqtt")) { + boolean logger = sharedPreferences.getBoolean("enable_mqtt", false); + Log.d(TAG, "Logger update: " + logger); + } + + + + } +} diff --git a/app/src/main/res/drawable/mqtt.xml b/app/src/main/res/drawable/mqtt.xml new file mode 100644 index 00000000..64b84c3f --- /dev/null +++ b/app/src/main/res/drawable/mqtt.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_iperf3_input.xml b/app/src/main/res/layout/fragment_iperf3_input.xml index 84d8b895..3a16e451 100644 --- a/app/src/main/res/layout/fragment_iperf3_input.xml +++ b/app/src/main/res/layout/fragment_iperf3_input.xml @@ -1,293 +1,287 @@ - - + xmlns:app="http://schemas.android.com/apk/res-auto"> - - - - - - - - - - - - - - - - - - - - -