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"> - - - - - - - - - - - - - - - - - - - - -