diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..5c98b42
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..681f41a
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/gradle_wrapper.xml b/.idea/libraries/gradle_wrapper.xml
new file mode 100644
index 0000000..dcde693
--- /dev/null
+++ b/.idea/libraries/gradle_wrapper.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/gradle_wrapper2.xml b/.idea/libraries/gradle_wrapper2.xml
new file mode 100644
index 0000000..dcde693
--- /dev/null
+++ b/.idea/libraries/gradle_wrapper2.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..e208459
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..d12e2ce
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/.idea/.name b/Speedtest-Android/.idea/.name
new file mode 100644
index 0000000..96108fe
--- /dev/null
+++ b/Speedtest-Android/.idea/.name
@@ -0,0 +1 @@
+speedtest-android-lib
\ No newline at end of file
diff --git a/Speedtest-Android/.idea/codeStyles/Project.xml b/Speedtest-Android/.idea/codeStyles/Project.xml
index 0d15693..663459a 100644
--- a/Speedtest-Android/.idea/codeStyles/Project.xml
+++ b/Speedtest-Android/.idea/codeStyles/Project.xml
@@ -3,18 +3,9 @@
-
diff --git a/Speedtest-Android/.idea/gradle.xml b/Speedtest-Android/.idea/gradle.xml
index 5cd135a..1424a3f 100644
--- a/Speedtest-Android/.idea/gradle.xml
+++ b/Speedtest-Android/.idea/gradle.xml
@@ -11,6 +11,9 @@
+
+
+
diff --git a/Speedtest-Android/app/.gitignore b/Speedtest-Android/app/.gitignore
index 796b96d..42afabf 100644
--- a/Speedtest-Android/app/.gitignore
+++ b/Speedtest-Android/app/.gitignore
@@ -1 +1 @@
-/build
+/build
\ No newline at end of file
diff --git a/Speedtest-Android/app/build.gradle b/Speedtest-Android/app/build.gradle
index 8ed4777..56dd273 100644
--- a/Speedtest-Android/app/build.gradle
+++ b/Speedtest-Android/app/build.gradle
@@ -1,16 +1,19 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
- buildToolsVersion "29.0.0"
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
defaultConfig {
- applicationId "your.name.here.speedtest"
- minSdkVersion 15
- targetSdkVersion 28
- versionCode 8
- versionName '1.2.2'
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ applicationId "com.fdossena.speedtest.core"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
+
buildTypes {
release {
minifyEnabled false
@@ -20,5 +23,11 @@ android {
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
-}
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation project(path: ':core')
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+}
\ No newline at end of file
diff --git a/Speedtest-Android/app/libs/core-debug.aar b/Speedtest-Android/app/libs/core-debug.aar
new file mode 100644
index 0000000..b5fa722
Binary files /dev/null and b/Speedtest-Android/app/libs/core-debug.aar differ
diff --git a/Speedtest-Android/app/proguard-rules.pro b/Speedtest-Android/app/proguard-rules.pro
index f1b4245..481bb43 100644
--- a/Speedtest-Android/app/proguard-rules.pro
+++ b/Speedtest-Android/app/proguard-rules.pro
@@ -18,4 +18,4 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
-#-renamesourcefileattribute SourceFile
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Speedtest-Android/app/src/main/AndroidManifest.xml b/Speedtest-Android/app/src/main/AndroidManifest.xml
index 685d61d..a2ab9f6 100644
--- a/Speedtest-Android/app/src/main/AndroidManifest.xml
+++ b/Speedtest-Android/app/src/main/AndroidManifest.xml
@@ -1,26 +1,26 @@
+package="your.name.here.speedtest">
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/Speedtest-Android/app/src/main/assets/ServerList.json b/Speedtest-Android/app/src/main/assets/ServerList.json
index 7719f0f..2a3ab86 100644
--- a/Speedtest-Android/app/src/main/assets/ServerList.json
+++ b/Speedtest-Android/app/src/main/assets/ServerList.json
@@ -1,10 +1,29 @@
[
{
"name":"Helsinki, Finland",
- "server":"//fi.openspeed.org",
+ "server":"http://fi.openspeed.org",
"dlURL":"garbage.php",
"ulURL":"empty.php",
"pingURL":"empty.php",
"getIpURL":"getIP.php"
+ },
+ {
+ "name":"Local",
+ "server":"http://192.168.12.43:8080",
+ "dlURL":"backend/garbage.php",
+ "ulURL":"backend/empty.php",
+ "pingURL":"backend/empty.php",
+ "getIpURL":"backend/getIP.php"
+ },
+ {
+ "name":"PlayIP",
+ "server":"http://170.238.84.8:8080/",
+ "dlURL":"backend/garbage.php",
+ "ulURL":"backend/empty.php",
+ "pingURL":"backend/empty.php",
+ "getIpURL":"backend/getIP.php"
}
+
+
+
]
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/download/DownloadStream.java b/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/download/DownloadStream.java
deleted file mode 100644
index b4acee5..0000000
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/download/DownloadStream.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.fdossena.speedtest.core.download;
-
-import com.fdossena.speedtest.core.config.SpeedtestConfig;
-import com.fdossena.speedtest.core.base.Connection;
-import com.fdossena.speedtest.core.base.Utils;
-import com.fdossena.speedtest.core.log.Logger;
-
-public abstract class DownloadStream {
- private String server, path;
- private int ckSize;
- private int connectTimeout, soTimeout, recvBuffer, sendBuffer;
- private Connection c=null;
- private Downloader downloader;
- private String errorHandlingMode= SpeedtestConfig.ONERROR_ATTEMPT_RESTART;
- private long currentDownloaded=0, previouslyDownloaded=0;
- private boolean stopASAP=false;
- private Logger log;
-
- public DownloadStream(String server, String path, int ckSize, String errorHandlingMode, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer, Logger log){
- this.server=server;
- this.path=path;
- this.ckSize=ckSize;
- this.errorHandlingMode=errorHandlingMode;
- this.connectTimeout=connectTimeout;
- this.soTimeout=soTimeout;
- this.recvBuffer=recvBuffer;
- this.sendBuffer=sendBuffer;
- this.log=log;
- init();
- }
-
- private void init(){
- if(stopASAP) return;
- new Thread(){
- public void run(){
- if(c!=null){
- try{c.close();}catch (Throwable t){}
- }
- if(downloader !=null) downloader.stopASAP();
- currentDownloaded=0;
- try {
- c = new Connection(server, connectTimeout, soTimeout, recvBuffer, sendBuffer);
- if(stopASAP){
- try{c.close();}catch (Throwable t){}
- return;
- }
- downloader =new Downloader(c,path,ckSize) {
- @Override
- public void onProgress(long downloaded) {
- currentDownloaded=downloaded;
- }
-
- @Override
- public void onError(String err) {
- log("A downloader died");
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_FAIL)){
- DownloadStream.this.onError(err);
- return;
- }
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_ATTEMPT_RESTART)||errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
- previouslyDownloaded+=currentDownloaded;
- Utils.sleep(100);
- init();
- }
- }
- };
- }catch (Throwable t){
- log("A downloader failed hard");
- try{c.close();}catch (Throwable t1){}
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
- Utils.sleep(100);
- init();
- }else onError(t.toString());
- }
- }
- }.start();
-
- }
-
- public abstract void onError(String err);
-
- public void stopASAP(){
- stopASAP=true;
- if(downloader !=null) downloader.stopASAP();
- }
-
- public long getTotalDownloaded(){
- return previouslyDownloaded+currentDownloaded;
- }
-
- public void resetDownloadCounter(){
- previouslyDownloaded=0;
- currentDownloaded=0;
- if(downloader !=null) downloader.resetDownloadCounter();
- }
-
- public void join(){
- while(downloader==null) Utils.sleep(0,100);
- try{downloader.join();}catch (Throwable t){}
- }
-
- private void log(String s){
- if(log!=null) log.l(s);
- }
-
-}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/ping/PingStream.java b/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/ping/PingStream.java
deleted file mode 100644
index e661d6f..0000000
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/ping/PingStream.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.fdossena.speedtest.core.ping;
-
-import com.fdossena.speedtest.core.config.SpeedtestConfig;
-import com.fdossena.speedtest.core.base.Connection;
-import com.fdossena.speedtest.core.base.Utils;
-import com.fdossena.speedtest.core.log.Logger;
-
-public abstract class PingStream {
- private String server, path;
- private int remainingPings=10;
- private int connectTimeout, soTimeout, recvBuffer, sendBuffer;
- private Connection c=null;
- private Pinger pinger;
- private String errorHandlingMode= SpeedtestConfig.ONERROR_ATTEMPT_RESTART;
- private boolean stopASAP=false;
- private Logger log;
-
- public PingStream(String server, String path, int pings, String errorHandlingMode, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer, Logger log){
- this.server=server;
- this.path=path;
- remainingPings=pings<1?1:pings;
- this.errorHandlingMode=errorHandlingMode;
- this.connectTimeout=connectTimeout;
- this.soTimeout=soTimeout;
- this.recvBuffer=recvBuffer;
- this.sendBuffer=sendBuffer;
- this.log=log;
- init();
- }
-
- private void init(){
- if(stopASAP) return;
- if(c!=null){
- try{c.close();}catch (Throwable t){}
- }
- new Thread(){
- public void run(){
- if(pinger !=null) pinger.stopASAP();
- if(remainingPings<=0) return;
- try {
- c = new Connection(server, connectTimeout, soTimeout, recvBuffer, sendBuffer);
- if(stopASAP){
- try{c.close();}catch (Throwable t){}
- return;
- }
- pinger =new Pinger(c,path) {
- @Override
- public boolean onPong(long ns) {
- boolean r=PingStream.this.onPong(ns);
- if(--remainingPings<=0||!r){
- onDone();
- return false;
- } else return true;
- }
-
- @Override
- public void onError(String err) {
- log("A pinger died");
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_FAIL)){
- PingStream.this.onError(err);
- return;
- }
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_ATTEMPT_RESTART)||errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
- Utils.sleep(100);
- init();
- }
- }
- };
- }catch (Throwable t){
- log("A pinger failed hard");
- try{c.close();}catch (Throwable t1){}
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
- Utils.sleep(100);
- init();
- }else onError(t.toString());
- }
- }
- }.start();
- }
-
- public abstract void onError(String err);
- public abstract boolean onPong(long ns);
- public abstract void onDone();
-
- public void stopASAP(){
- stopASAP=true;
- if(pinger !=null) pinger.stopASAP();
- }
-
- public void join(){
- while(pinger==null) Utils.sleep(0,100);
- try{pinger.join();}catch (Throwable t){}
- }
-
- private void log(String s){
- if(log!=null) log.l(s);
- }
-
-}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/upload/UploadStream.java b/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/upload/UploadStream.java
deleted file mode 100644
index 4ae3c02..0000000
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/upload/UploadStream.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.fdossena.speedtest.core.upload;
-
-import com.fdossena.speedtest.core.base.Connection;
-import com.fdossena.speedtest.core.base.Utils;
-import com.fdossena.speedtest.core.config.SpeedtestConfig;
-import com.fdossena.speedtest.core.log.Logger;
-
-public abstract class UploadStream {
- private String server, path;
- private int ckSize;
- private int connectTimeout, soTimeout, recvBuffer, sendBuffer;
- private Connection c=null;
- private Uploader uploader;
- private String errorHandlingMode= SpeedtestConfig.ONERROR_ATTEMPT_RESTART;
- private long currentUploaded=0, previouslyUploaded=0;
- private boolean stopASAP=false;
- private Logger log;
-
- public UploadStream(String server, String path, int ckSize, String errorHandlingMode, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer, Logger log){
- this.server=server;
- this.path=path;
- this.ckSize=ckSize;
- this.errorHandlingMode=errorHandlingMode;
- this.connectTimeout=connectTimeout;
- this.soTimeout=soTimeout;
- this.recvBuffer=recvBuffer;
- this.sendBuffer=sendBuffer;
- this.log=log;
- init();
- }
-
- private void init(){
- if(stopASAP) return;
- new Thread(){
- public void run(){
- if(c!=null){
- try{c.close();}catch (Throwable t){}
- }
- if(uploader !=null) uploader.stopASAP();
- currentUploaded=0;
- try {
- c = new Connection(server, connectTimeout, soTimeout, recvBuffer, sendBuffer);
- if(stopASAP){
- try{c.close();}catch (Throwable t){}
- return;
- }
- uploader =new Uploader(c,path,ckSize) {
- @Override
- public void onProgress(long uploaded) {
- currentUploaded=uploaded;
- }
-
- @Override
- public void onError(String err) {
- log("An uploader died");
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_FAIL)){
- UploadStream.this.onError(err);
- return;
- }
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_ATTEMPT_RESTART)||errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
- previouslyUploaded+=currentUploaded;
- Utils.sleep(100);
- init();
- }
- }
- };
- }catch (Throwable t){
- log("An uploader failed hard");
- try{c.close();}catch (Throwable t1){}
- if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
- Utils.sleep(100);
- init();
- }else onError(t.toString());
- }
- }
- }.start();
- }
-
- public abstract void onError(String err);
-
- public void stopASAP(){
- stopASAP=true;
- if(uploader !=null) uploader.stopASAP();
- }
-
- public long getTotalUploaded(){
- return previouslyUploaded+currentUploaded;
- }
-
- public void resetUploadCounter(){
- previouslyUploaded=0;
- currentUploaded=0;
- if(uploader !=null) uploader.resetUploadCounter();
- }
-
- public void join(){
- while(uploader==null) Utils.sleep(0,100);
- try{uploader.join();}catch (Throwable t){}
- }
-
- private void log(String s){
- if(log!=null) log.l(s);
- }
-
-}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/upload/Uploader.java b/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/upload/Uploader.java
deleted file mode 100644
index 34a0a9f..0000000
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/upload/Uploader.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.fdossena.speedtest.core.upload;
-
-import java.io.OutputStream;
-import java.util.Random;
-
-import com.fdossena.speedtest.core.base.Connection;
-
-public abstract class Uploader extends Thread{
- private Connection c;
- private String path;
- private boolean stopASAP=false, resetASAP=false;
- private long totUploaded=0;
- private byte[] garbage;
-
- public Uploader(Connection c, String path, int ckSize){
- this.c=c;
- this.path=path;
- garbage=new byte[ckSize*1048576];
- Random r=new Random(System.nanoTime());
- r.nextBytes(garbage);
- start();
- }
-
- private static final int BUFFER_SIZE=16384;
- public void run(){
- try{
- String s=path;
- long lastProgressEvent=System.currentTimeMillis();
- OutputStream out=c.getOutputStream();
- byte[] buf=new byte[BUFFER_SIZE];
- for(;;){
- if(stopASAP) break;
- c.POST(s,true,"application/octet-stream",garbage.length);
- for(int offset=0;offset=garbage.length)?(garbage.length-offset):BUFFER_SIZE;
- out.write(garbage,offset,l);
- if(stopASAP) break;
- if(resetASAP){
- totUploaded=0;
- resetASAP=false;
- }
- totUploaded+=l;
- if(System.currentTimeMillis()-lastProgressEvent>200){
- lastProgressEvent=System.currentTimeMillis();
- onProgress(totUploaded);
- }
- }
- if(stopASAP) break;
- while(!c.readLineUnbuffered().trim().isEmpty());
- }
- c.close();
- }catch(Throwable t){
- try{c.close();}catch(Throwable t1){}
- onError(t.toString());
- }
- }
-
- public void stopASAP(){
- this.stopASAP=true;
- }
-
- public abstract void onProgress(long uploaded);
- public abstract void onError(String err);
-
- public void resetUploadCounter(){
- resetASAP=true;
- }
-
- public long getUploaded() {
- return resetASAP?0:totUploaded;
- }
-}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/ui/MainActivity.java b/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/ui/MainActivity.java
index 99d0f5c..b6c5d2d 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/ui/MainActivity.java
+++ b/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/ui/MainActivity.java
@@ -402,6 +402,10 @@ public void onClick(View v) {
}
});
}
+ @Override
+ public void onNonCriticalFailure(String err)
+ {
+ }
});
}
diff --git a/Speedtest-Android/app/src/test/java/com/fdossena/speedtest/core/ExampleUnitTest.java b/Speedtest-Android/app/src/test/java/com/fdossena/speedtest/core/ExampleUnitTest.java
new file mode 100644
index 0000000..cbdf3d0
--- /dev/null
+++ b/Speedtest-Android/app/src/test/java/com/fdossena/speedtest/core/ExampleUnitTest.java
@@ -0,0 +1,19 @@
+package com.fdossena.speedtest.core;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest
+{
+ @Test
+ public void addition_isCorrect()
+ {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Speedtest-Android/build.gradle b/Speedtest-Android/build.gradle
index 95a3996..6754c23 100644
--- a/Speedtest-Android/build.gradle
+++ b/Speedtest-Android/build.gradle
@@ -1,14 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
buildscript {
repositories {
google()
jcenter()
-
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.0.1'
-
+ classpath "com.android.tools.build:gradle:4.0.1"
+
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -18,10 +16,9 @@ allprojects {
repositories {
google()
jcenter()
-
}
}
task clean(type: Delete) {
delete rootProject.buildDir
-}
+}
\ No newline at end of file
diff --git a/Speedtest-Android/core/.gitignore b/Speedtest-Android/core/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/Speedtest-Android/core/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Speedtest-Android/core/build.gradle b/Speedtest-Android/core/build.gradle
new file mode 100644
index 0000000..85db2d7
--- /dev/null
+++ b/Speedtest-Android/core/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+}
\ No newline at end of file
diff --git a/Speedtest-Android/core/consumer-rules.pro b/Speedtest-Android/core/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/Speedtest-Android/core/proguard-rules.pro b/Speedtest-Android/core/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/Speedtest-Android/core/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Speedtest-Android/core/src/androidTest/java/com/fdossena/speedtest/core/ExampleInstrumentedTest.java b/Speedtest-Android/core/src/androidTest/java/com/fdossena/speedtest/core/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..5d079fa
--- /dev/null
+++ b/Speedtest-Android/core/src/androidTest/java/com/fdossena/speedtest/core/ExampleInstrumentedTest.java
@@ -0,0 +1,28 @@
+package com.fdossena.speedtest.core;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest
+{
+ @Test
+ public void useAppContext()
+ {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.fdossena.speedtest.core.test", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/Speedtest-Android/core/src/main/AndroidManifest.xml b/Speedtest-Android/core/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..98d1fa7
--- /dev/null
+++ b/Speedtest-Android/core/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+ /
+
\ No newline at end of file
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/Speedtest.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/Speedtest.java
similarity index 90%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/Speedtest.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/Speedtest.java
index 03a52c8..2968fd0 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/Speedtest.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/Speedtest.java
@@ -16,6 +16,8 @@
import com.fdossena.speedtest.core.serverSelector.TestPoint;
import com.fdossena.speedtest.core.worker.SpeedtestWorker;
+import javax.net.SocketFactory;
+
public class Speedtest {
private ArrayList servers=new ArrayList<>();
private TestPoint selectedServer=null;
@@ -26,14 +28,23 @@ public class Speedtest {
private Object mutex=new Object();
private String originalExtra="";
+ SocketFactory clientSocketFactory;
- public Speedtest(){
+ public Speedtest() {
+ this(null);
+ }
+ public Speedtest(SocketFactory clientSocketFactory){
+ this.clientSocketFactory = clientSocketFactory;
}
+ public void setSpeedtestSocketfactory(SocketFactory clientSocketFactory) {
+ if(state==4 || state==2) throw new IllegalStateException("Cannot change the socket factory at this moment");
+ this.clientSocketFactory = clientSocketFactory;
+ }
public void setSpeedtestConfig(SpeedtestConfig c){
synchronized (mutex){
- if(state!=0) throw new IllegalStateException("Cannot change config at this moment");
+ if(state==2 || state==4) throw new IllegalStateException("Cannot change config at this moment");
config=c.clone();
String extra=config.getTelemetry_extra();
if(extra!=null&&!extra.isEmpty()) originalExtra=extra;
@@ -142,7 +153,7 @@ public void selectServer(final ServerSelectedHandler callback){
if (state == 2) throw new IllegalStateException("Server selection is in progress");
if (state > 2) throw new IllegalStateException("Server already selected");
state = 2;
- ss = new ServerSelector(getTestPoints(), config.getPing_connectTimeout()) {
+ ss = new ServerSelector(getTestPoints(), config.getPing_connectTimeout(), clientSocketFactory) {
@Override
public void onServerSelected(TestPoint server) {
selectedServer = server;
@@ -159,6 +170,7 @@ public void onServerSelected(TestPoint server) {
public void setSelectedServer(TestPoint t){
synchronized (mutex) {
if (state == 2) throw new IllegalStateException("Server selection is in progress");
+ if (state == 4) throw new IllegalStateException("Test already running");
if (t == null) throw new IllegalArgumentException("t is null");
selectedServer = t;
state = 3;
@@ -179,7 +191,7 @@ public void start(final SpeedtestHandler callback){
config.setTelemetry_extra(extra.toString());
} catch (Throwable t) {
}
- st = new SpeedtestWorker(selectedServer, config, telemetryConfig) {
+ st = new SpeedtestWorker(selectedServer, config, telemetryConfig, clientSocketFactory) {
@Override
public void onDownloadUpdate(double dl, double progress) {
callback.onDownloadUpdate(dl, progress);
@@ -222,6 +234,11 @@ public void onCriticalFailure(String err) {
}
callback.onCriticalFailure(err);
}
+ @Override
+ public void onNonCriticalFailure(String err) {
+ callback.onNonCriticalFailure(err);
+ }
+
};
}
}
@@ -255,5 +272,7 @@ public static abstract class SpeedtestHandler{
public abstract void onTestIDReceived(String id, String shareURL);
public abstract void onEnd();
public abstract void onCriticalFailure(String err);
+
+ public abstract void onNonCriticalFailure(String err);
}
}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/base/Connection.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/base/Connection.java
similarity index 91%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/base/Connection.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/base/Connection.java
index fad0bf0..4ed02b0 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/base/Connection.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/base/Connection.java
@@ -24,7 +24,7 @@ public class Connection {
private static final String USER_AGENT="Speedtest-Android/1.2 (SDK "+Build.VERSION.SDK_INT+"; "+Build.PRODUCT+"; Android "+Build.VERSION.RELEASE+")",
LOCALE= Build.VERSION.SDK_INT>=21?Locale.getDefault().toLanguageTag():null;
- public Connection(String url, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer){
+ public Connection(String url, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer, SocketFactory clientSocketFactory){
boolean tryHTTP=false, tryHTTPS=false;
Locale.getDefault().toString();
if(url.startsWith("http://")){
@@ -60,7 +60,7 @@ public Connection(String url, int connectTimeout, int soTimeout, int recvBuffer,
}
try{
if(tryHTTPS){
- SocketFactory factory = SSLSocketFactory.getDefault();
+ SocketFactory factory = clientSocketFactory!=null ? clientSocketFactory : SSLSocketFactory.getDefault();
socket=factory.createSocket();
if(connectTimeout>0){
socket.connect(new InetSocketAddress(host, port==-1?443:port),connectTimeout);
@@ -69,10 +69,13 @@ public Connection(String url, int connectTimeout, int soTimeout, int recvBuffer,
}
mode=MODE_HTTPS;
}
- }catch(Throwable t){}
+ }catch(Throwable t)
+ {
+ t.printStackTrace();
+ }
try{
if(tryHTTP){
- SocketFactory factory = SocketFactory.getDefault();
+ SocketFactory factory = clientSocketFactory!=null ? clientSocketFactory : SocketFactory.getDefault();
socket=factory.createSocket();
if(connectTimeout>0) {
socket.connect(new InetSocketAddress(host, port == -1 ? 80 : port), connectTimeout);
@@ -81,7 +84,11 @@ public Connection(String url, int connectTimeout, int soTimeout, int recvBuffer,
}
mode=MODE_HTTP;
}
- }catch(Throwable t){}
+ }
+ catch(Throwable t)
+ {
+ t.printStackTrace();
+ }
if(mode==MODE_NOT_SET) throw new IllegalStateException("Failed to connect");
if(soTimeout>0) {
try {
@@ -101,8 +108,8 @@ public Connection(String url, int connectTimeout, int soTimeout, int recvBuffer,
}
private static final int DEFAULT_CONNECT_TIMEOUT=2000, DEFAULT_SO_TIMEOUT=5000;
- public Connection(String url){
- this(url,DEFAULT_CONNECT_TIMEOUT,DEFAULT_SO_TIMEOUT,-1,-1);
+ public Connection(String url, SocketFactory clientSocketFactory) {
+ this(url,DEFAULT_CONNECT_TIMEOUT,DEFAULT_SO_TIMEOUT,-1,-1, clientSocketFactory);
}
public InputStream getInputStream(){
@@ -163,6 +170,9 @@ public void GET(String path, boolean keepAlive) throws Exception{
public void POST(String path, boolean keepAlive, String contentType, long contentLength) throws Exception{
try{
+ socket.setPerformancePreferences (0, 0, 1);
+ socket.setTrafficClass(0x08);
+ socket.setTcpNoDelay(false);
if(!path.startsWith("/")) path="/"+path;
PrintStream ps=getPrintStream();
ps.print("POST "+path+" HTTP/1.1\r\n");
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/base/Utils.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/base/Utils.java
similarity index 100%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/base/Utils.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/base/Utils.java
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/config/SpeedtestConfig.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/config/SpeedtestConfig.java
similarity index 96%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/config/SpeedtestConfig.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/config/SpeedtestConfig.java
index c7d7584..b16d6e4 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/config/SpeedtestConfig.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/config/SpeedtestConfig.java
@@ -4,15 +4,15 @@
import org.json.JSONObject;
public class SpeedtestConfig {
- private int dl_ckSize=100, ul_ckSize=20;
- private int dl_parallelStreams=3, ul_parallelStreams=3;
+ private int dl_ckSize=100, ul_ckSize=2;
+ private int dl_parallelStreams=3, ul_parallelStreams=5;
private int dl_streamDelay=300, ul_streamDelay=300;
private double dl_graceTime=1.5, ul_graceTime=1.5;
private int dl_connectTimeout=5000, dl_soTimeout=10000, ul_connectTimeout=5000, ul_soTimeout=10000, ping_connectTimeout=2000, ping_soTimeout=5000;
private int dl_recvBuffer=-1, dl_sendBuffer=-1, ul_recvBuffer=-1, ul_sendBuffer=16384, ping_recvBuffer=-1, ping_sendBuffer=-1;
private String errorHandlingMode=ONERROR_ATTEMPT_RESTART;
public static final String ONERROR_FAIL="fail", ONERROR_ATTEMPT_RESTART="attempt-restart", ONERROR_MUST_RESTART="must-restart";
- private int time_dl_max=15, time_ul_max=15;
+ private int time_dl_max=15, time_ul_max=30;
private boolean time_auto=true;
private int count_ping=10;
private String telemetry_extra="";
@@ -22,6 +22,7 @@ public class SpeedtestConfig {
public static final String DISTANCE_NO="no", DISTANCE_MILES="mi", DISTANCE_KM="km";
private boolean useMebibits=false;
private String test_order="IP_D_U";
+ private int max_number_of_restarts = 10;
private void check(){
if(dl_ckSize<1) throw new IllegalArgumentException("dl_ckSize must be at least 1");
@@ -47,7 +48,7 @@ public SpeedtestConfig(){
check();
}
- public SpeedtestConfig(int dl_ckSize, int ul_ckSize, int dl_parallelStreams, int ul_parallelStreams, int dl_streamDelay, int ul_streamDelay, double dl_graceTime, double ul_graceTime, int dl_connectTimeout, int dl_soTimeout, int ul_connectTimeout, int ul_soTimeout, int ping_connectTimeout, int ping_soTimeout, int dl_recvBuffer, int dl_sendBuffer, int ul_recvBuffer, int ul_sendBuffer, int ping_recvBuffer, int ping_sendBuffer, String errorHandlingMode, int time_dl_max, int time_ul_max, boolean time_auto, int count_ping, String telemetry_extra, double overheadCompensationFactor, boolean getIP_isp, String getIP_distance, boolean useMebibits, String test_order) {
+ public SpeedtestConfig(int dl_ckSize, int ul_ckSize, int dl_parallelStreams, int ul_parallelStreams, int dl_streamDelay, int ul_streamDelay, double dl_graceTime, double ul_graceTime, int dl_connectTimeout, int dl_soTimeout, int ul_connectTimeout, int ul_soTimeout, int ping_connectTimeout, int ping_soTimeout, int dl_recvBuffer, int dl_sendBuffer, int ul_recvBuffer, int ul_sendBuffer, int ping_recvBuffer, int ping_sendBuffer, String errorHandlingMode, int time_dl_max, int time_ul_max, boolean time_auto, int count_ping, String telemetry_extra, double overheadCompensationFactor, boolean getIP_isp, String getIP_distance, boolean useMebibits, String test_order, int max_number_of_restarts) {
this.dl_ckSize = dl_ckSize;
this.ul_ckSize = ul_ckSize;
this.dl_parallelStreams = dl_parallelStreams;
@@ -79,6 +80,7 @@ public SpeedtestConfig(int dl_ckSize, int ul_ckSize, int dl_parallelStreams, int
this.getIP_distance = getIP_distance;
this.useMebibits = useMebibits;
this.test_order = test_order;
+ this.max_number_of_restarts = max_number_of_restarts;
check();
}
@@ -122,6 +124,7 @@ public SpeedtestConfig(JSONObject json){
if (json.has("getIP_distance")) this.getIP_distance = json.getString("getIP_distance");
if (json.has("test_order")) this.test_order = json.getString("test_order");
if (json.has("useMebibits")) this.useMebibits = json.getBoolean("useMebibits");
+ if (json.has("max_number_of_restarts")) this.max_number_of_restarts = json.getInt("max_number_of_restarts");
check();
}catch(JSONException t){
throw new IllegalArgumentException("Invalid JSON ("+t.toString()+")");
@@ -409,7 +412,11 @@ public void setTest_order(String test_order) {
this.test_order = test_order;
}
+ public int getMax_number_of_restarts() {
+ return max_number_of_restarts;
+ }
+
public SpeedtestConfig clone(){
- return new SpeedtestConfig(dl_ckSize, ul_ckSize, dl_parallelStreams, ul_parallelStreams, dl_streamDelay, ul_streamDelay, dl_graceTime, ul_graceTime, dl_connectTimeout, dl_soTimeout, ul_connectTimeout, ul_soTimeout, ping_connectTimeout, ping_soTimeout, dl_recvBuffer, dl_sendBuffer, ul_recvBuffer, ul_sendBuffer, ping_recvBuffer, ping_sendBuffer, errorHandlingMode, time_dl_max, time_ul_max, time_auto, count_ping, telemetry_extra, overheadCompensationFactor, getIP_isp, getIP_distance, useMebibits, test_order);
+ return new SpeedtestConfig(dl_ckSize, ul_ckSize, dl_parallelStreams, ul_parallelStreams, dl_streamDelay, ul_streamDelay, dl_graceTime, ul_graceTime, dl_connectTimeout, dl_soTimeout, ul_connectTimeout, ul_soTimeout, ping_connectTimeout, ping_soTimeout, dl_recvBuffer, dl_sendBuffer, ul_recvBuffer, ul_sendBuffer, ping_recvBuffer, ping_sendBuffer, errorHandlingMode, time_dl_max, time_ul_max, time_auto, count_ping, telemetry_extra, overheadCompensationFactor, getIP_isp, getIP_distance, useMebibits, test_order, max_number_of_restarts);
}
}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/config/TelemetryConfig.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/config/TelemetryConfig.java
similarity index 100%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/config/TelemetryConfig.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/config/TelemetryConfig.java
diff --git a/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/download/DownloadStream.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/download/DownloadStream.java
new file mode 100644
index 0000000..3ce6ad1
--- /dev/null
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/download/DownloadStream.java
@@ -0,0 +1,173 @@
+package com.fdossena.speedtest.core.download;
+
+import com.fdossena.speedtest.core.config.SpeedtestConfig;
+import com.fdossena.speedtest.core.base.Connection;
+import com.fdossena.speedtest.core.base.Utils;
+import com.fdossena.speedtest.core.log.Logger;
+import com.fdossena.speedtest.core.upload.UploadStream;
+
+import javax.net.SocketFactory;
+
+public abstract class DownloadStream {
+ private String server, path;
+ private int ckSize;
+ private int connectTimeout, soTimeout, recvBuffer, sendBuffer;
+ private Connection c=null;
+ private Downloader downloader;
+ private String errorHandlingMode= SpeedtestConfig.ONERROR_ATTEMPT_RESTART;
+ private long currentDownloaded=0, previouslyDownloaded=0;
+ private boolean stopASAP=false;
+ private Logger log;
+ private int max_number_of_restarts;
+ private int numEnded;
+ private int numStarted;
+ SocketFactory clientSocketFactory;
+
+ public DownloadStream(String server, String path, int ckSize, String errorHandlingMode, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer, int max_number_of_restarts, Logger log, SocketFactory clientSocketFactory) {
+ this.server=server;
+ this.path=path;
+ this.ckSize=ckSize;
+ this.errorHandlingMode=errorHandlingMode;
+ this.connectTimeout=connectTimeout;
+ this.soTimeout=soTimeout;
+ this.recvBuffer=recvBuffer;
+ this.sendBuffer=sendBuffer;
+ this.log=log;
+ this.max_number_of_restarts = max_number_of_restarts;
+ numEnded = 0;
+ numStarted = 0;
+ this.clientSocketFactory = clientSocketFactory;
+ init();
+ }
+
+ private void init(){
+ synchronized (this)
+ {
+ numStarted++;
+ // If this method was called from the onError method of the downloader, a new downloader will be created to replace that downloader.
+ // In this case, numStarted was incremented right before numEnded was incremented by the call to onEnd() of the downloader.
+ // The difference between numStarted and numEnded went to 2, so numEnded cannot become equal to numStart before the new downloader ends.
+ // If this method was called from the constructor, then numStarted was incremented in the creator thread and stopASAP() and join()
+ // can only be called after this increment.
+ }
+ new Thread("DownloadStream"){
+ public void run(){
+ synchronized (DownloadStream.this) {
+ currentDownloaded=0;
+ }
+ try {
+ c = new Connection(server, connectTimeout, soTimeout, recvBuffer, sendBuffer, clientSocketFactory);
+ Downloader newDownloader =new Downloader(c,path,ckSize) {
+ @Override
+ public void onProgress(long downloaded) {
+ synchronized (DownloadStream.this) {
+ currentDownloaded = downloaded;
+ }
+ }
+ @Override
+ public void onError(String err) {
+ log("A downloader died");
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_FAIL)){
+ DownloadStream.this.onError(err);
+ return;
+ }
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_ATTEMPT_RESTART)||errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
+ previouslyDownloaded+=currentDownloaded;
+ synchronized (DownloadStream.this) {
+ previouslyDownloaded+=currentDownloaded;
+ }
+ if (max_number_of_restarts > numStarted)
+ {
+ DownloadStream.this.onWarning(err);
+ init();
+ }
+ else
+ {
+ DownloadStream.this.onError(err);
+ return;
+ }
+ }
+ }
+ @Override
+ public void onWarning(String err)
+ {
+ DownloadStream.this.onWarning(err);
+ }
+ @Override
+ public void onEnd() {
+ synchronized(DownloadStream.this) {
+ DownloadStream.this.numEnded++; // if the difference between numStarted and numEnded goes to zero, the download test is over, so notify the waiting thread (see join() method)
+ DownloadStream.this.notify(); // the difference only goes to zero if an downloader ends without calling onError() and creating a new downloader
+ }
+ }
+ };
+ synchronized(DownloadStream.this)
+ {
+ if (!stopASAP) {
+ downloader = newDownloader; // from ths point on, any calls to stopASAP will stop the new downloader
+ }
+ else
+ // DownStream was stopped (by a call to stopASAP()) during the creation of the new Downloader or
+ // right before and either told the old downloader to stop (by a call to downloader's stopASAP())
+ // or told no downloader to stop because there was no old downloader. Anyway, the new downloader was
+ // not told to stop.
+ // Nobody will tell the downstream to stop again, so stop the new downloader immediately.
+ newDownloader.stopASAP();
+ }
+ }catch (Throwable t){
+ log("A Downloader failed hard");
+ try{c.close();} catch (Throwable t1){} // If the Downloader failed to be created, it may not close the connection
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
+ synchronized (DownloadStream.this) {
+ if (max_number_of_restarts > numStarted)
+ init();
+ numEnded++; // only marks the end after init() has marked the start of the new downloader
+ if (numEnded == numStarted)
+ DownloadStream.this.notify(); // notifies the waiting thread, because the download test over
+ }
+ } else {
+ onError(t.toString());
+ synchronized (DownloadStream.this) {
+ numEnded++;
+ if (numEnded == numStarted)
+ DownloadStream.this.notify(); // notifies the waiting thread, because the downloader test is over
+ }
+ }
+ }
+ }
+ }.start();
+ }
+
+ public abstract void onError(String err);
+
+ public abstract void onWarning(String err);
+
+ public synchronized void stopASAP() {
+ stopASAP=true;
+ if(downloader !=null)
+ downloader.stopASAP();
+ }
+
+ public synchronized long getTotalDownloaded() {
+ return previouslyDownloaded+currentDownloaded;
+ }
+
+ public synchronized void resetDownloadCounter() {
+ previouslyDownloaded=0;
+ currentDownloaded=0;
+ if(downloader !=null)
+ downloader.resetDownloadCounter();
+ }
+
+ public void join(){
+ synchronized(this) {
+ while (numStarted > numEnded) // if this test fails, all created uploaders have ended
+ try { wait(); } catch(InterruptedException e) {};
+ }
+ }
+
+ private void log(String s){
+ if(log!=null) log.l(s);
+ }
+
+}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/download/Downloader.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/download/Downloader.java
similarity index 65%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/download/Downloader.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/download/Downloader.java
index a876ac9..fd42496 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/download/Downloader.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/download/Downloader.java
@@ -13,6 +13,7 @@ public abstract class Downloader extends Thread{
private long totDownloaded=0;
public Downloader(Connection c, String path, int ckSize){
+ super("DownLoader");
this.c=c;
this.path=path;
this.ckSize=ckSize<1?1:ckSize;
@@ -21,47 +22,58 @@ public Downloader(Connection c, String path, int ckSize){
private static final int BUFFER_SIZE=16384;
public void run(){
+ InputStream in = null;
try{
String s=path;
s+= Utils.url_sep(s)+"ckSize="+ckSize;
long lastProgressEvent=System.currentTimeMillis();
long ckBytes=ckSize*1048576, newRequestThreshold=ckBytes/4;
long bytesLeft=0;
- InputStream in=c.getInputStream();
+ in=c.getInputStream();
byte[] buf=new byte[BUFFER_SIZE];
for(;;){
- if(stopASAP) break;
+ synchronized(this) { if(stopASAP) break; }
if(bytesLeft<=newRequestThreshold){
c.GET(s, true);
bytesLeft+=ckBytes;
}
- if(stopASAP) break;
+ synchronized(this) { if(stopASAP) break; }
int l=in.read(buf);
- if(stopASAP) break;
- bytesLeft-=l;
- if(resetASAP){
- totDownloaded=0;
- resetASAP=false;
+ long curTotDownloaded;
+ synchronized(this) {
+ if (stopASAP) break;
+ bytesLeft -= l;
+ if (resetASAP) {
+ totDownloaded = 0;
+ resetASAP = false;
+ }
+ totDownloaded+=l;
+ curTotDownloaded = totDownloaded;
}
- totDownloaded+=l;
+
if(System.currentTimeMillis()-lastProgressEvent>200){
lastProgressEvent=System.currentTimeMillis();
- onProgress(totDownloaded);
+ onProgress(curTotDownloaded); // makes the call outside the critical region using a local variable as parameter
}
}
- c.close();
}catch(Throwable t){
- try{c.close();}catch(Throwable t1){}
onError(t.toString());
}
+ finally {
+ try { if (in!=null) in.close(); } catch(Throwable t1){}
+ try{c.close();}catch(Throwable t1){}
+ onEnd();
+ }
}
+ public abstract void onEnd();
public void stopASAP(){
this.stopASAP=true;
}
public abstract void onProgress(long downloaded);
public abstract void onError(String err);
+ public abstract void onWarning(String err);
public void resetDownloadCounter(){
resetASAP=true;
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/getIP/GetIP.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/getIP/GetIP.java
similarity index 100%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/getIP/GetIP.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/getIP/GetIP.java
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/log/Logger.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/log/Logger.java
similarity index 100%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/log/Logger.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/log/Logger.java
diff --git a/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/ping/PingStream.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/ping/PingStream.java
new file mode 100644
index 0000000..ec0645c
--- /dev/null
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/ping/PingStream.java
@@ -0,0 +1,145 @@
+package com.fdossena.speedtest.core.ping;
+
+import com.fdossena.speedtest.core.config.SpeedtestConfig;
+import com.fdossena.speedtest.core.base.Connection;
+import com.fdossena.speedtest.core.base.Utils;
+import com.fdossena.speedtest.core.log.Logger;
+import com.fdossena.speedtest.core.upload.UploadStream;
+
+import javax.net.SocketFactory;
+
+public abstract class PingStream {
+ private String server, path;
+ private int remainingPings=10;
+ private int connectTimeout, soTimeout, recvBuffer, sendBuffer;
+ private Connection c=null;
+ private Pinger pinger;
+ private String errorHandlingMode= SpeedtestConfig.ONERROR_ATTEMPT_RESTART;
+ private boolean stopASAP=false;
+ private Logger log;
+ private int max_number_of_restarts;
+ private int numEnded;
+ private int numStarted;
+ SocketFactory clientSocketFactory;
+
+ public PingStream(String server, String path, int pings, String errorHandlingMode, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer, int max_number_of_restarts, Logger log, SocketFactory clientSocketFactory) {
+ this.server=server;
+ this.path=path;
+ remainingPings=pings<1?1:pings;
+ this.errorHandlingMode=errorHandlingMode;
+ this.connectTimeout=connectTimeout;
+ this.soTimeout=soTimeout;
+ this.recvBuffer=recvBuffer;
+ this.sendBuffer=sendBuffer;
+ this.log=log;
+ this.max_number_of_restarts = max_number_of_restarts;
+ numEnded = 0;
+ numStarted = 0;
+ this.clientSocketFactory = clientSocketFactory;
+ init();
+ }
+
+ private void init(){
+ synchronized (this)
+ {
+ numStarted++;
+ // If this method was called from the onError method of the pinger, a new pinger will be created to replace that pinger.
+ // In this case, numStarted was incremented right before numEnded was incremented by the call to onEnd() of the pinger.
+ // The difference between numStarted and numEnded went to 2, so numEnded cannot become equal to numStart before the new pinger ends.
+ // If this method was called from the constructor, then numStarted was incremented in the creator thread and stopASAP() and join()
+ // can only be called after this increment.
+ }
+ new Thread("PingStream"){
+ public void run(){
+ if(remainingPings<=0) return;
+ try {
+ c = new Connection(server, connectTimeout, soTimeout, recvBuffer, sendBuffer, clientSocketFactory);
+ Pinger newPinger =new Pinger(c,path) {
+ @Override
+ public boolean onPong(long ns) {
+ boolean r=PingStream.this.onPong(ns);
+ if(--remainingPings<=0||!r){
+ onDone();
+ return false;
+ } else return true;
+ }
+ @Override
+ public void onError(String err) {
+ log("A pinger died");
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_FAIL)){
+ PingStream.this.onError(err);
+ return;
+ }
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_ATTEMPT_RESTART)||errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
+ if (max_number_of_restarts > numStarted)
+ init();
+ }
+ }
+ @Override
+ public void onEnd()
+ {
+ synchronized(PingStream.this) {
+ numEnded++; // if the difference between numStarted and numEnded goes to zero, the ping test is over, so notify the waiting thread (see join() method)
+ if (numEnded == numStarted) // the difference only goes to zero if a pinger ends without calling onError() and creating a new pinger
+ PingStream.this.notify(); // notifies the waiting thread, because the ping test is over
+ }
+ }
+ };
+ synchronized(PingStream.this)
+ {
+ if (!stopASAP) {
+ pinger = newPinger; // from ths point on, any calls to stopASAP will stop the new pinger
+ }
+ else
+ // PingStream was stopped (by a call to stopASAP()) during the creation of the new Pinger or
+ // right before and either told the old pinger to stop (by a call to pinger's stopASAP())
+ // or told no pinger to stop because there was no old pinger. Anyway, the new pinger was
+ // not told to stop.
+ // Nobody will tell the pingstream to stop again, so stop the new pinger immediately.
+ newPinger.stopASAP();
+ }
+ }catch (Throwable t){
+ log("A pinger failed hard");
+ try{c.close();}catch (Throwable t1){} // If the Pinger failed to be created, it may not close the connection
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
+ synchronized (PingStream.this) {
+ if (max_number_of_restarts > numStarted)
+ init();
+ numEnded++; // only marks the end after init() has marked the start of the new pinger
+ if (numEnded == numStarted)
+ PingStream.this.notify(); // notifies the waiting thread, because the ping test over
+ }
+ } else {
+ onError(t.toString());
+ synchronized (PingStream.this) {
+ numEnded++;
+ if (numEnded == numStarted)
+ PingStream.this.notify(); // notifies the waiting thread, because the ping test is over
+ }
+ }
+ }
+ }
+ }.start();
+ }
+
+ public abstract void onError(String err);
+ public abstract boolean onPong(long ns);
+ public abstract void onDone();
+
+ public synchronized void stopASAP(){
+ stopASAP=true;
+ if(pinger !=null) pinger.stopASAP();
+ }
+
+ public void join() {
+ synchronized(this) {
+ while (numStarted > numEnded) // if this test fails, all created pingers have ended
+ try { wait(); } catch(InterruptedException e) {};
+ }
+ }
+
+ private void log(String s){
+ if(log!=null) log.l(s);
+ }
+
+}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/ping/Pinger.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/ping/Pinger.java
similarity index 76%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/ping/Pinger.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/ping/Pinger.java
index 8b0c534..5f1c703 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/ping/Pinger.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/ping/Pinger.java
@@ -10,19 +10,21 @@ public abstract class Pinger extends Thread{
private boolean stopASAP=false;
public Pinger(Connection c, String path){
+ super("Pinger");
this.c=c;
this.path=path;
start();
}
public void run(){
+ InputStream in = null;
try{
String s=path;
- InputStream in=c.getInputStream();
+ in=c.getInputStream();
for(;;){
- if(stopASAP) break;
+ synchronized(this) { if(stopASAP) break; }
c.GET(s,true);
- if(stopASAP) break;
+ synchronized(this) { if(stopASAP) break; }
long t=System.nanoTime();
boolean chunked=false;
boolean ok=false;
@@ -39,20 +41,25 @@ public void run(){
}
if(!ok) throw new Exception("Did not get a 200");
t=System.nanoTime()-t;
- if(stopASAP) break;
+ synchronized(this) { if(stopASAP) break; }
if(!onPong(t/2)) break;
}
- c.close();
}catch(Throwable t){
+ try { if (in!=null) in.close(); } catch(Throwable t1){}
try{c.close();}catch(Throwable t1){}
onError(t.toString());
}
+ finally {
+ c.close();
+ onEnd();
+ }
}
+ public abstract void onEnd();
public abstract boolean onPong(long ns);
public abstract void onError(String err);
- public void stopASAP(){
+ public synchronized void stopASAP(){
this.stopASAP=true;
}
}
\ No newline at end of file
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/serverSelector/ServerSelector.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/serverSelector/ServerSelector.java
similarity index 93%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/serverSelector/ServerSelector.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/serverSelector/ServerSelector.java
index b353fdb..bb1f6d7 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/serverSelector/ServerSelector.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/serverSelector/ServerSelector.java
@@ -7,6 +7,8 @@
import com.fdossena.speedtest.core.config.SpeedtestConfig;
import com.fdossena.speedtest.core.ping.PingStream;
+import javax.net.SocketFactory;
+
public abstract class ServerSelector {
private ArrayList servers=new ArrayList<>();
private static final int PARALLELISM=6;
@@ -16,10 +18,12 @@ public abstract class ServerSelector {
private int timeout;
private static final int PINGS=3, SLOW_THRESHOLD=500;
private boolean stopASAP=false;
+ SocketFactory clientSocketFactory;
- public ServerSelector(TestPoint[] servers, int timeout){
+ public ServerSelector(TestPoint[] servers, int timeout, SocketFactory clientSocketFactory){
addTestPoints(servers);
this.timeout=timeout;
+ this.clientSocketFactory = clientSocketFactory;
}
public void addTestPoint(TestPoint t){
if(state!=NOT_STARTED) throw new IllegalStateException("Cannot add test points at this time");
@@ -72,7 +76,7 @@ private void next(){
return;
}
final TestPoint tp=servers.get(tpPointer++);
- PingStream ps=new PingStream(tp.getServer(),tp.getPingURL(),PINGS, SpeedtestConfig.ONERROR_FAIL,timeout,timeout,-1,-1,null) {
+ PingStream ps=new PingStream(tp.getServer(),tp.getPingURL(),PINGS, SpeedtestConfig.ONERROR_FAIL,timeout,timeout,-1,-1, 1,null, clientSocketFactory) {
@Override
public void onError(String err) {
tp.ping=-1;
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/serverSelector/TestPoint.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/serverSelector/TestPoint.java
similarity index 80%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/serverSelector/TestPoint.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/serverSelector/TestPoint.java
index 1693097..6f4585d 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/serverSelector/TestPoint.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/serverSelector/TestPoint.java
@@ -62,4 +62,23 @@ public String getGetIpURL() {
public float getPing() {
return ping;
}
+
+ public JSONObject toJSON()
+ {
+ JSONObject json = new JSONObject();
+ try
+ {
+ json.put("name", name);
+ json.put("server", server);
+ json.put("dlURL", dlURL);
+ json.put("ulURL", ulURL);
+ json.put("pingURL", pingURL);
+ json.put("getIpURL", getIpURL);
+ return json;
+ }
+ catch (JSONException e)
+ {
+ return json;
+ }
+ }
}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/telemetry/Telemetry.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/telemetry/Telemetry.java
similarity index 100%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/telemetry/Telemetry.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/telemetry/Telemetry.java
diff --git a/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/upload/UploadStream.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/upload/UploadStream.java
new file mode 100644
index 0000000..400cfdd
--- /dev/null
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/upload/UploadStream.java
@@ -0,0 +1,190 @@
+package com.fdossena.speedtest.core.upload;
+
+import com.fdossena.speedtest.core.base.Connection;
+import com.fdossena.speedtest.core.base.Utils;
+import com.fdossena.speedtest.core.config.SpeedtestConfig;
+import com.fdossena.speedtest.core.log.Logger;
+import com.fdossena.speedtest.core.ping.PingStream;
+
+import javax.net.SocketFactory;
+
+public abstract class UploadStream {
+ private String server, path;
+ private int ckSize;
+ private int connectTimeout, soTimeout, recvBuffer, sendBuffer;
+ private Connection c=null;
+ private Uploader uploader;
+ private String errorHandlingMode= SpeedtestConfig.ONERROR_ATTEMPT_RESTART;
+ private long currentUploaded=0, previouslyUploaded=0;
+ private boolean stopASAP=false;
+ private Logger log;
+ private int numEnded;
+ private int numStarted;
+ private int max_number_of_restarts;
+
+ static int nid = 0;
+ int id;
+ SocketFactory clientSocketFactory;
+
+ public UploadStream(String server, String path, int ckSize, String errorHandlingMode, int connectTimeout, int soTimeout, int recvBuffer, int sendBuffer, int max_number_of_restarts, Logger log, SocketFactory clientSocketFactory) {
+ this.server=server;
+ this.path=path;
+ this.ckSize=ckSize;
+ this.errorHandlingMode=errorHandlingMode;
+ this.connectTimeout=connectTimeout;
+ this.soTimeout=soTimeout;
+ this.recvBuffer=recvBuffer;
+ this.sendBuffer=sendBuffer;
+ this.log=log;
+ this.max_number_of_restarts = max_number_of_restarts;
+ numEnded = 0;
+ numStarted = 0;
+ id = nid++;
+ this.clientSocketFactory = clientSocketFactory;
+ init();
+ }
+
+ private void init(){
+ synchronized (this)
+ {
+ numStarted++;
+ // If this method was called from the onError method of the uploader, a new uploader will be created to replace that downloader.
+ // In this case, numStarted was incremented right before numEnded was incremented by the call to onEnd() of the uploader.
+ // The difference between numStarted and numEnded went to 2, so numEnded cannot become equal to numStart before the new uploader ends.
+ // If this method was called from the constructor, then numStarted was incremented in the creator thread and stopASAP() and join()
+ // can only be called after this increment.
+ }
+ new Thread("UploadStream"){
+ public void run(){
+ synchronized (UploadStream.this) {
+ currentUploaded = 0;
+ }
+ try {
+ c = new Connection(server, connectTimeout, soTimeout, recvBuffer, sendBuffer, clientSocketFactory);
+ Uploader newUploader =new Uploader(c,path,ckSize) {
+ @Override
+ public void onProgress(long uploaded) {
+ synchronized (UploadStream.this) {
+ currentUploaded = uploaded;
+ System.out.println("Uploadstream update");
+ }
+ }
+ @Override
+ public void onError(String err) {
+ log("An uploader died");
+ UploadStream.this.onError(err);
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_FAIL)){
+ UploadStream.this.onError(err);
+ return;
+ }
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_ATTEMPT_RESTART)||errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
+ synchronized (UploadStream.this) {
+ previouslyUploaded+=currentUploaded;
+ }
+ if (max_number_of_restarts > numStarted)
+ {
+ UploadStream.this.onWarning(err);
+ init();
+ }
+ else
+ {
+ UploadStream.this.onError(err);
+ return;
+ }
+ }
+ }
+ @Override
+ public void onWarning(String err)
+ {
+ UploadStream.this.onWarning(err);
+ }
+ @Override
+ public void onEnd() {
+ synchronized(UploadStream.this) {
+ numEnded++; // if the difference between numStarted and numEnded goes to zero, the upload test is over, so notify the waiting thread (see join() method)
+ System.out.println("up onEnd "+id+" "+numStarted+" "+numEnded);
+ if (numEnded==numStarted) // the difference only goes to zero if an uploader ends without calling onError() and creating a new uploader
+ UploadStream.this.notify(); // notifies the waiting thread, because the upload test over
+ }
+ }
+ };
+ synchronized(UploadStream.this)
+ {
+ if (!stopASAP) {
+ uploader = newUploader; // from ths point on, any calls to stopASAP will stop the new uploader
+ }
+ else
+ // UpStream was stopped (by a call to stopASAP()) during the creation of the new Uploader or
+ // right before and either told the old uploader to stop (by a call to uploader's stopASAP())
+ // or told no uploader to stop because there was no old uploader. Anyway, the new uploader was
+ // not told to stop.
+ // Nobody will tell the upstream to stop again, so stop the new uploader immediately.
+ newUploader.stopASAP();
+ }
+ } catch (Throwable t){
+ log("An uploader failed hard");
+ try{c.close();}catch (Throwable t1){} // If the Uploader failed to be created, it may not close the connection
+ if(errorHandlingMode.equals(SpeedtestConfig.ONERROR_MUST_RESTART)){
+ synchronized (UploadStream.this) {
+ if (max_number_of_restarts > numStarted)
+ init();
+ numEnded++; // only marks the end after init() has marked the start of the new uploader
+ if (numEnded == numStarted)
+ UploadStream.this.notify(); // notifies the waiting thread, because the upload test over
+ }
+ } else {
+ onError(t.toString());
+ synchronized (UploadStream.this) {
+ numEnded++;
+ if (numEnded == numStarted)
+ UploadStream.this.notify(); // notifies the waiting thread, because the upload test may be over
+ }
+ }
+ }
+ }
+ }.start();
+ }
+
+ public abstract void onError(String err);
+
+ public abstract void onWarning(String err);
+
+ public synchronized void stopASAP() {
+ stopASAP=true;
+ if(uploader !=null)
+ uploader.stopASAP();
+ }
+
+ public synchronized long getTotalUploaded() {
+ return previouslyUploaded+currentUploaded;
+ }
+
+ public synchronized void resetUploadCounter(){
+ previouslyUploaded=0;
+ currentUploaded=0;
+ if(uploader !=null)
+ uploader.resetUploadCounter();
+ }
+
+ public void join(){
+ synchronized(this) {
+ while (numStarted > numEnded) // if this test fails, all created uploaders have ended
+ {
+ System.out.println("up join "+id+" "+numStarted+" "+numEnded);
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ System.out.println("up join "+id+" "+numStarted+" "+numEnded);
+ }
+ }
+
+ private void log(String s){
+ if(log!=null) log.l(s);
+ }
+
+}
diff --git a/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/upload/Uploader.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/upload/Uploader.java
new file mode 100644
index 0000000..74c4fb0
--- /dev/null
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/upload/Uploader.java
@@ -0,0 +1,116 @@
+package com.fdossena.speedtest.core.upload;
+
+import java.io.OutputStream;
+import java.util.Random;
+
+import com.fdossena.speedtest.core.base.Connection;
+
+public abstract class Uploader extends Thread{
+ private Connection c;
+ private String path;
+ private boolean stopASAP=false, resetASAP=false;
+ private long totUploaded=0;
+ private byte[] garbage;
+
+ public Uploader(Connection c, String path, int ckSize){
+ super("Uploader");
+ this.c=c;
+ this.path=path;
+ garbage=new byte[ckSize*1048576];
+ Random r=new Random(System.nanoTime());
+ long time1 = System.currentTimeMillis();
+ r.nextBytes(garbage);
+ long time2 = System.currentTimeMillis();
+ System.out.println("Random byte generation took "+(time2-time1)+" ms for "+garbage.length+" bytes");
+ start();
+ }
+/*
+ public void run()
+ {
+ onEnd();
+ }
+*/
+ private static final int BUFFER_SIZE=16384;
+ public void run(){
+ System.out.println("Uploader run");
+ OutputStream out = null;
+ try
+ {
+ String s=path;
+ long lastProgressEvent=System.currentTimeMillis();
+ out=c.getOutputStream();
+ //byte[] buf=new byte[BUFFER_SIZE];
+ int n = 0;
+ for(;;){
+ synchronized(this) { if(stopASAP) break; }
+ System.out.println("Uploader loop");
+ c.POST(s,true,"application/octet-stream",garbage.length);
+ for(int offset=0;offset=garbage.length)?(garbage.length-offset):BUFFER_SIZE;
+ //System.out.println("Uploader before out");
+ out.write(garbage,offset,l);
+ //System.out.println("Uploader after out");
+ long curTotUploaded;
+ synchronized(this) {
+ if(stopASAP) break;
+ if(resetASAP) {
+ totUploaded = 0;
+ resetASAP = false;
+ }
+ totUploaded+=l;
+ curTotUploaded = totUploaded;
+ }
+ if(System.currentTimeMillis()-lastProgressEvent>200){
+ lastProgressEvent=System.currentTimeMillis();
+ System.out.println("Uploader before on progress");
+ onProgress(curTotUploaded); // makes the call outside the critical region using a local variable as parameter
+ }
+ //System.out.println("Loop "+n+" "+offset);
+ n++;
+ }
+ synchronized(this) { if(stopASAP) break; }
+ for(;;)
+ {
+ String lin = c.readLineUnbuffered();
+ System.out.println(lin);
+ if (lin==null || lin.trim().isEmpty())
+ break;
+ }
+ }
+ } catch(Throwable t){
+ t.printStackTrace();
+ onError(t.toString());
+ } finally {
+ try {
+ if (out!=null) out.close();
+ }
+ catch(Throwable t1) {
+ t1.printStackTrace();
+ }
+ try {
+ c.close();
+ } catch (Throwable t1)
+ {
+ t1.printStackTrace();
+ }
+ onEnd();
+ }
+ }
+
+ public synchronized void stopASAP(){
+ this.stopASAP=true;
+ }
+
+ public abstract void onEnd();
+ public abstract void onProgress(long uploaded);
+ public abstract void onError(String err);
+ public abstract void onWarning(String err);
+
+ public synchronized void resetUploadCounter(){
+ resetASAP=true;
+ }
+ public synchronized long getUploaded() {
+ return resetASAP?0:totUploaded;
+ }
+}
diff --git a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/worker/SpeedtestWorker.java b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/worker/SpeedtestWorker.java
similarity index 89%
rename from Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/worker/SpeedtestWorker.java
rename to Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/worker/SpeedtestWorker.java
index 727b379..18c7892 100644
--- a/Speedtest-Android/app/src/main/java/com/fdossena/speedtest/core/worker/SpeedtestWorker.java
+++ b/Speedtest-Android/core/src/main/java/com/fdossena/speedtest/core/worker/SpeedtestWorker.java
@@ -16,6 +16,8 @@
import java.util.Locale;
+import javax.net.SocketFactory;
+
public abstract class SpeedtestWorker extends Thread{
private TestPoint backend;
private SpeedtestConfig config;
@@ -24,11 +26,13 @@ public abstract class SpeedtestWorker extends Thread{
private double dl=-1, ul=-1, ping=-1, jitter=-1;
private String ipIsp="";
private Logger log=new Logger();
+ SocketFactory clientSocketFactory;
- public SpeedtestWorker(TestPoint backend, SpeedtestConfig config, TelemetryConfig telemetryConfig){
+ public SpeedtestWorker(TestPoint backend, SpeedtestConfig config, TelemetryConfig telemetryConfig, SocketFactory clientSocketFactory){
this.backend=backend;
this.config=config==null?new SpeedtestConfig():config;
this.telemetryConfig=telemetryConfig==null?new TelemetryConfig():telemetryConfig;
+ this.clientSocketFactory = clientSocketFactory;
start();
}
@@ -42,6 +46,7 @@ public void run(){
if (t == 'D') dlTest();
if (t == 'U') ulTest();
if (t == 'P') pingTest();
+ if (t == 'P') pingTest();
}
}catch (Throwable t){
onCriticalFailure(t.toString());
@@ -49,7 +54,8 @@ public void run(){
try{
sendTelemetry();
}catch (Throwable t){}
- onEnd();
+ onEnd(); // guaranteed to be called because no exceptions can scape the prior try/catches,
+ // and no early returns exist in this method. So, no need to use finally.
}
private boolean getIPCalled=false;
@@ -58,7 +64,7 @@ private void getIP(){
final long start=System.currentTimeMillis();
Connection c = null;
try {
- c = new Connection(backend.getServer(), config.getPing_connectTimeout(), config.getPing_soTimeout(), -1, -1);
+ c = new Connection(backend.getServer(), config.getPing_connectTimeout(), config.getPing_soTimeout(), -1, -1, clientSocketFactory);
} catch (Throwable t) {
if (config.getErrorHandlingMode().equals(SpeedtestConfig.ONERROR_FAIL)){
abort();
@@ -94,13 +100,19 @@ private void dlTest(){
onDownloadUpdate(0,0);
DownloadStream[] streams=new DownloadStream[config.getDl_parallelStreams()];
for(int i=0;iTesting documentation
+ */
+public class ExampleUnitTest
+{
+ @Test
+ public void addition_isCorrect()
+ {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/.gitignore b/Speedtest-Android/examplespeedtest/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/build.gradle b/Speedtest-Android/examplespeedtest/build.gradle
new file mode 100644
index 0000000..30db295
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/build.gradle
@@ -0,0 +1,34 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "com.playip.speedtest.examplespeedtest"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
+ implementation project(path: ':core')
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+}
diff --git a/Speedtest-Android/examplespeedtest/proguard-rules.pro b/Speedtest-Android/examplespeedtest/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/androidTest/java/com/playip/speedtest/examplespeedtest/ExampleInstrumentedTest.java b/Speedtest-Android/examplespeedtest/src/androidTest/java/com/playip/speedtest/examplespeedtest/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..0dd4e7f
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/androidTest/java/com/playip/speedtest/examplespeedtest/ExampleInstrumentedTest.java
@@ -0,0 +1,28 @@
+package com.playip.speedtest.examplespeedtest;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest
+{
+ @Test
+ public void useAppContext()
+ {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.playip.speedtest.examplespeedtest", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/AndroidManifest.xml b/Speedtest-Android/examplespeedtest/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..60153fd
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/java/com/playip/speedtest/examplespeedtest/ExampleSpeedTest.java b/Speedtest-Android/examplespeedtest/src/main/java/com/playip/speedtest/examplespeedtest/ExampleSpeedTest.java
new file mode 100644
index 0000000..357040a
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/java/com/playip/speedtest/examplespeedtest/ExampleSpeedTest.java
@@ -0,0 +1,211 @@
+/**
+ *
+ * This example is released under the MIT license, so it can be copied, modified and used in
+ * proprietary software.
+ *
+ * Note the the speed test core library is released under LGLP. PAY ATTENTION.
+ *
+ */
+package com.playip.speedtest.examplespeedtest;
+
+import com.fdossena.speedtest.core.Speedtest;
+import com.fdossena.speedtest.core.config.SpeedtestConfig;
+import com.fdossena.speedtest.core.config.TelemetryConfig;
+import com.fdossena.speedtest.core.serverSelector.TestPoint;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import javax.net.SocketFactory;
+
+public class ExampleSpeedTest
+{
+ public interface SpeedTestLog
+ {
+ void speedTestlog(String s);
+ }
+
+ Speedtest st = null;
+ boolean testGoingOn = false;
+ SpeedTestResult res;
+ ExampleSpeedTest()
+ {
+ SpeedtestConfig stConfig = new SpeedtestConfig();
+ TelemetryConfig telemetryConfig = new TelemetryConfig();
+ st=new Speedtest();
+ st.setSpeedtestConfig(stConfig);
+ st.setTelemetryConfig(telemetryConfig);
+ }
+ static class SpeedTestResult
+ {
+ double ping;
+ double jitter;
+ double downloadSpeed;
+ double uploadSpeed;
+ String ipInfo;
+ String error;
+ double progress_ping;
+ double progress_jitter;
+ double progress_downloadSpeed;
+ double progress_uploadSpeed;
+ boolean ended;
+ TestPoint tp;
+
+ SpeedTestResult(TestPoint tp)
+ {
+ this.tp = tp;
+ }
+ public synchronized String toString()
+ {
+ return toJSON().toString();
+ }
+ public synchronized JSONObject toJSON()
+ {
+ try
+ {
+ JSONObject res = new JSONObject();
+ res.put("ping", ping);
+ res.put("jitter", jitter);
+ res.put("downloadSpeed",downloadSpeed);
+ res.put("uploadSpeed",uploadSpeed);
+ res.put("ipInfo",ipInfo);
+ res.put("error",error);
+ res.put("progress_ping", progress_ping);
+ res.put("progress_jitter", progress_jitter);
+ res.put("progress_downloadSpeed",progress_downloadSpeed);
+ res.put("progress_uploadSpeed",progress_uploadSpeed);
+ res.put("ended",ended);
+ res.put("server",tp.toJSON());
+ return res;
+ }
+ catch (JSONException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ public synchronized double getPing() { return ping; }
+ public synchronized void setPing(double ping) { this.ping = ping; }
+ public synchronized double getJitter() { return jitter; }
+ public synchronized void setJitter(double jitter) { this.jitter = jitter; }
+ public synchronized double getDownloadSpeed() { return downloadSpeed; }
+ public synchronized void setDownloadSpeed(double downloadSpeed) { this.downloadSpeed = downloadSpeed; }
+ public synchronized double getUploadSpeed() { return uploadSpeed; }
+ public synchronized void setUploadSpeed(double uploadSpeed) { this.uploadSpeed = uploadSpeed; }
+ public synchronized String getIpInfo() { return ipInfo; }
+ public synchronized void setIpInfo(String ipInfo) { this.ipInfo = ipInfo; }
+ public synchronized String getError() { return error; }
+ public synchronized void setError(String error) { this.error = error; }
+ public synchronized double getProgress_ping() { return progress_ping; }
+ public synchronized void setProgress_ping(double progress_ping) { this.progress_ping = progress_ping; }
+ public synchronized double getProgress_jitter() { return progress_jitter; }
+ public synchronized void setProgress_jitter(double progress_jitter) { this.progress_jitter = progress_jitter; }
+ public synchronized double getProgress_downloadSpeed() { return progress_downloadSpeed; }
+ public synchronized void setProgress_downloadSpeed(double progress_downloadSpeed) { this.progress_downloadSpeed = progress_downloadSpeed; }
+ public synchronized double getProgress_uploadSpeed() { return progress_uploadSpeed; }
+ public synchronized void setProgress_uploadSpeed(double progress_uploadSpeed) { this.progress_uploadSpeed = progress_uploadSpeed; }
+ public synchronized boolean isEnded() { return ended; }
+ public synchronized void setEnded(boolean ended) { this.ended = ended; }
+ }
+ public SpeedTestResult test(TestPoint tp, final SpeedTestLog logger)
+ {
+
+ synchronized(this)
+ {
+ if (testGoingOn)
+ {
+ logger.speedTestlog("Test in progress");
+ logger.speedTestlog(res.toString());
+ return res;
+ }
+ testGoingOn = true;
+ res = new SpeedTestResult(tp);
+ }
+ Speedtest.SpeedtestHandler speedTestHandler = new Speedtest.SpeedtestHandler()
+ {
+ @Override
+ public void onDownloadUpdate(final double dl, final double progress)
+ {
+ res.setDownloadSpeed(dl);
+ res.setProgress_downloadSpeed(progress);
+ //logger.speedTestlog(res.toString());
+ }
+ @Override
+ public void onUploadUpdate(final double ul, final double progress)
+ {
+ res.setUploadSpeed(ul);
+ res.setProgress_uploadSpeed(progress);
+ //logger.speedTestlog(res.toString());
+ }
+ @Override
+ public void onPingJitterUpdate(final double ping, final double jitter, final double progress)
+ {
+ res.setPing(ping);
+ res.setJitter(jitter);
+ res.setProgress_jitter(progress);
+ res.setProgress_ping(progress);
+ //logger.speedTestlog(res.toString());
+ }
+
+ @Override
+ public void onIPInfoUpdate(final String ipInfo)
+ {
+ res.setIpInfo(ipInfo);
+ logger.speedTestlog(res.toString());
+ }
+
+ @Override
+ public void onTestIDReceived(final String id, final String shareURL)
+ {
+ }
+
+ @Override
+ public void onEnd()
+ {
+ logger.speedTestlog("Speed test ended");
+ res.setEnded(true);
+ logger.speedTestlog(res.toString());
+ synchronized(res)
+ {
+ res.notify();
+ }
+ }
+
+ @Override
+ public void onCriticalFailure(String err)
+ {
+ res.setError(err);
+ logger.speedTestlog(res.toString());
+ }
+ @Override
+ public void onNonCriticalFailure(String err)
+ {
+ logger.speedTestlog(err);
+ }
+ };
+ try
+ {
+ st.setSpeedtestConfig(new SpeedtestConfig()); // you can change some parameters here
+ st.setSpeedtestSocketfactory(SocketFactory.getDefault()); // you can choose a different SocketFactory, for example to test mobile speed even if the default connection is wifi
+ st.setSelectedServer(tp);
+ st.start(speedTestHandler);
+ }
+ catch(Throwable th)
+ {
+ res.setEnded(true);
+ res.setError(th.getMessage());
+ logger.speedTestlog(res.toString());
+ System.out.println("Speed test could not start");
+ }
+ synchronized(res)
+ {
+ while (!res.isEnded())
+ try { res.wait();} catch(InterruptedException ignored) {}
+ }
+ synchronized(this)
+ {
+ testGoingOn = false;
+ }
+ logger.speedTestlog("Speed test exited");
+ return res;
+ }
+}
diff --git a/Speedtest-Android/examplespeedtest/src/main/java/com/playip/speedtest/examplespeedtest/MainActivity.java b/Speedtest-Android/examplespeedtest/src/main/java/com/playip/speedtest/examplespeedtest/MainActivity.java
new file mode 100644
index 0000000..fb87faa
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/java/com/playip/speedtest/examplespeedtest/MainActivity.java
@@ -0,0 +1,101 @@
+package com.playip.speedtest.examplespeedtest;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.method.ScrollingMovementMethod;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.fdossena.speedtest.core.serverSelector.TestPoint;
+
+public class MainActivity extends AppCompatActivity implements ExampleSpeedTest.SpeedTestLog
+{
+ Button button_run;
+ TextView tvReport;
+ ExampleSpeedTest exampleSpeedTest;
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ exampleSpeedTest = new ExampleSpeedTest();
+ setContentView(R.layout.activity_main);
+ button_run = ((Button)findViewById(R.id.button_run));
+ tvReport = ((TextView)findViewById(R.id.tv_report));
+ tvReport.setMovementMethod(new ScrollingMovementMethod());
+ button_run.setOnClickListener
+ (
+ new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View view)
+ {
+ new Thread(new TaskSpeed()).start();
+ }
+ }
+ );
+
+ }
+ @Override
+ public void speedTestlog(final String s)
+ {
+
+ final Handler UIHandler = new Handler(Looper.getMainLooper());
+ UIHandler .post
+ (
+ new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ tvReport.append(s+"\n");
+ System.out.println(s);
+ }
+ }
+ );
+ }
+ private class TaskSpeed implements Runnable
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+/*
+ String name = "Helsinki, Finland";
+ String server = "//fi.openspeed.org";
+ String dlURL = "garbage.php";
+ String ulURL = "empty.php";
+ String pingURL = "empty.php";
+ String getIpURL = "getIP.php";
+*/
+ String name = "PlayIP";
+ String server = "http://170.238.84.8:8080/";
+ String dlURL = "backend/garbage.php";
+ String ulURL = "backend/empty.php";
+ String pingURL = "backend/empty.php";
+ String getIpURL = "backend/getIP.php";
+
+/*
+ String name = "Local";
+ String server = "http://192.168.12.43:8080";
+ String dlURL = "backend/garbage.php";
+ String ulURL = "backend/empty.php";
+ String pingURL = "backend/empty.php";
+ String getIpURL = "backend/getIP.php";
+*/
+
+
+ TestPoint tp = new TestPoint(name, server, dlURL, ulURL, pingURL, getIpURL);
+ exampleSpeedTest.test(tp, MainActivity.this);
+ }
+ catch (Throwable e)
+ {
+ }
+ }
+ }
+
+}
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Speedtest-Android/examplespeedtest/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/drawable/ic_launcher_background.xml b/Speedtest-Android/examplespeedtest/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/layout/activity_main.xml b/Speedtest-Android/examplespeedtest/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..c4973bd
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-hdpi/ic_launcher.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-mdpi/ic_launcher.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xhdpi/ic_launcher.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/Speedtest-Android/examplespeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/values/colors.xml b/Speedtest-Android/examplespeedtest/src/main/res/values/colors.xml
new file mode 100644
index 0000000..4faecfa
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #6200EE
+ #3700B3
+ #03DAC5
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/values/strings.xml b/Speedtest-Android/examplespeedtest/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a1a4dc2
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ ExampleSpeedTest
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/main/res/values/styles.xml b/Speedtest-Android/examplespeedtest/src/main/res/values/styles.xml
new file mode 100644
index 0000000..fac9291
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/examplespeedtest/src/test/java/com/playip/speedtest/examplespeedtest/ExampleUnitTest.java b/Speedtest-Android/examplespeedtest/src/test/java/com/playip/speedtest/examplespeedtest/ExampleUnitTest.java
new file mode 100644
index 0000000..1d4a171
--- /dev/null
+++ b/Speedtest-Android/examplespeedtest/src/test/java/com/playip/speedtest/examplespeedtest/ExampleUnitTest.java
@@ -0,0 +1,19 @@
+package com.playip.speedtest.examplespeedtest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest
+{
+ @Test
+ public void addition_isCorrect()
+ {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Speedtest-Android/gradle.properties b/Speedtest-Android/gradle.properties
index 82618ce..c52ac9b 100644
--- a/Speedtest-Android/gradle.properties
+++ b/Speedtest-Android/gradle.properties
@@ -6,10 +6,14 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
+org.gradle.jvmargs=-Xmx2048m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
-
-
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
\ No newline at end of file
diff --git a/Speedtest-Android/gradle/wrapper/gradle-wrapper.properties b/Speedtest-Android/gradle/wrapper/gradle-wrapper.properties
index 4142380..48508cc 100644
--- a/Speedtest-Android/gradle/wrapper/gradle-wrapper.properties
+++ b/Speedtest-Android/gradle/wrapper/gradle-wrapper.properties
@@ -1,4 +1,4 @@
-#Sun Sep 27 20:23:36 CEST 2020
+#Mon Oct 26 20:35:22 BRT 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/Speedtest-Android/httpverybasicspeedtest/.gitignore b/Speedtest-Android/httpverybasicspeedtest/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/build.gradle b/Speedtest-Android/httpverybasicspeedtest/build.gradle
new file mode 100644
index 0000000..3a62b91
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/build.gradle
@@ -0,0 +1,33 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "com.fdossena.speedtest.httpverybasicspeedtest"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/proguard-rules.pro b/Speedtest-Android/httpverybasicspeedtest/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/androidTest/java/com/fdossena/speedtest/httpverybasicspeedtest/ExampleInstrumentedTest.java b/Speedtest-Android/httpverybasicspeedtest/src/androidTest/java/com/fdossena/speedtest/httpverybasicspeedtest/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..efff812
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/androidTest/java/com/fdossena/speedtest/httpverybasicspeedtest/ExampleInstrumentedTest.java
@@ -0,0 +1,28 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest
+{
+ @Test
+ public void useAppContext()
+ {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.fdossena.speedtest.httpverybasicspeedtest", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/AndroidManifest.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..442f6ad
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/DownUpSpeedTest.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/DownUpSpeedTest.java
new file mode 100644
index 0000000..c20575c
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/DownUpSpeedTest.java
@@ -0,0 +1,167 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import javax.net.SocketFactory;
+
+abstract public class DownUpSpeedTest
+{
+ long numBytesMoved;
+ boolean stop;
+ boolean running;
+ boolean ended;
+ long timeIni;
+ long timeEnd;
+ Throwable error;
+ DownUpSpeedTest()
+ {
+ }
+ synchronized boolean moved(long numBytes)
+ {
+ numBytesMoved += numBytes;
+ return stop;
+ }
+ synchronized void setError(Throwable error)
+ {
+ this.error = error;
+ }
+ class DownUpSpeedResult
+ {
+ double speed;
+ double percentage;
+ boolean ended;
+ Throwable error;
+ public DownUpSpeedResult(double speed, Throwable error,double percentage, boolean ended)
+ {
+ this.speed = speed;
+ this.error = error;
+ this.ended = ended;
+ this.percentage = percentage;
+ }
+ public String toString()
+ {
+ return "Ended = " + ended +
+ " Percentage = " + percentage +
+ " Speed = " + speed +
+ " Error = " + (error==null?"None":error.getMessage());
+ }
+ }
+ public synchronized DownUpSpeedResult getResult() // the client code can keep calling this method to get partial results
+ {
+ long time2 = ended ? timeEnd : System.currentTimeMillis();
+ long delta = (time2 - timeIni) / 1000;
+ if (!ended && ! running || delta <=0)
+ return new DownUpSpeedResult(0, error, 0, false);
+ double speed = numBytesMoved * 8.0 / delta / 1024 / 1024;
+ if (Double.isInfinite(speed))
+ throw new RuntimeException("Velocidade infinita");
+ int overhead = 1;
+ double perc = (running ? Math.min(delta * 1.0 / (beginDelay + testLength + overhead), 1.0) : 1.0) * 100;
+ return new DownUpSpeedResult(speed, error, perc, ended);
+ }
+ abstract class SocketTestRunnable implements Runnable
+ {
+ SpeedTestListener log;
+ Socket socket;
+ String host;
+ int port;
+ String path;
+ SocketTestRunnable(SpeedTestListener log, String host, int port, String path, Socket socket) throws IOException
+ {
+ this.log = log;
+ this.socket = socket;
+ this.host = host;
+ this.port = port;
+ this.path = path;
+ }
+ }
+
+ abstract SocketTestRunnable getSocketTestRunnable(SpeedTestListener log, String host, int port, String path, Socket socket) throws IOException;
+
+ int beginDelay = 10;
+ int testLength = 30;
+
+ public synchronized void clear()
+ {
+ stop = false;
+ error = null;
+ ended = false;
+ }
+ public void test(String host, int port, String path, int nt, SpeedTestListener log, SocketFactory clientSocketFactory)
+ {
+ System.out.println("Test DownUp Called");
+ synchronized (this)
+ {
+
+ if (running)
+ return; // does not not start again if still running
+ running = true;
+ clear();
+ }
+ try
+ {
+ Thread[] th = new Thread[nt];
+ for (int t = 0; t < nt; t++)
+ {
+ SocketFactory factory = clientSocketFactory==null ? SocketFactory.getDefault() : clientSocketFactory;
+ Socket socket = factory.createSocket();
+ socket.connect(new InetSocketAddress(host, port));
+ th[t] = new Thread(getSocketTestRunnable(log,host, port, path, socket));
+ }
+ timeIni = System.currentTimeMillis();
+ for (int t = 0; t < nt; t++)
+ th[t].start();
+ try
+ {
+ Thread.sleep(1000 * beginDelay);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ synchronized (this)
+ {
+ numBytesMoved = 0; // to ignore initial speed
+ }
+ timeIni = System.currentTimeMillis(); // to ignore initial speed
+ try
+ {
+ Thread.sleep(1000 * testLength);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ synchronized (this)
+ {
+ stop = true;
+ }
+ for (int t = 0; t < nt; t++)
+ {
+ try
+ {
+ th[t].join();
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ timeEnd = System.currentTimeMillis();
+ System.out.println("Test DownUp Ended "+(timeEnd-timeIni)/1000+" timeIni "+timeIni+" timeEnd "+timeEnd );
+ }
+ catch(Throwable th)
+ {
+ setError(th);
+ }
+ finally
+ {
+ synchronized (this)
+ {
+ running = false;
+ ended = true;
+ }
+ log.speedTestEnded();
+ }
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/DownloadSpeedTest.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/DownloadSpeedTest.java
new file mode 100644
index 0000000..819c531
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/DownloadSpeedTest.java
@@ -0,0 +1,79 @@
+/**
+ *
+ * This example is released under the MIT license, so it can be copied, modified and used in
+ * proprietary software.
+ *
+ * Note the the speed test core library is released under LGLP. PAY ATTENTION.
+ *
+ */
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public class DownloadSpeedTest extends DownUpSpeedTest
+{
+
+ public DownloadSpeedTest()
+ {
+ super();
+ }
+
+ class Downloader extends SocketTestRunnable
+ {
+ public Downloader(SpeedTestListener log, String host, int port, String path, Socket socket) throws IOException
+ {
+ super(log, host, port, path, socket);
+ }
+ public void run()
+ {
+ testDownloadIntern(socket, host, port, path);
+ }
+ }
+ SocketTestRunnable getSocketTestRunnable(SpeedTestListener log, String host, int port, String path, Socket socket) throws IOException
+ {
+ return new Downloader(log, host, port, path, socket);
+ }
+
+ int n = 100;
+ int sizeChunks = n*1048576;
+
+ private void testDownloadIntern(Socket socket, String host, int port, String path)
+ {
+ try
+ {
+ int bufSize = 64 * 1024;
+ byte[] buf=new byte[bufSize];
+ path+= "?ckSize="+n; // This is to tell the server the number of chunks it should send. Each chunk has always 1048576 bytes
+ OutputStream out = socket.getOutputStream();
+ InputStream in = socket.getInputStream();
+ int newRequestThreshold=sizeChunks/4;
+ long bytesLeft=0;
+ for(;;)
+ {
+ if(bytesLeft<=newRequestThreshold) // send a new request before having read all bytes from the previous, in order to keep the flow
+ {
+ HTTPHelper.putGetHeadersInStream(out, host, path);
+ bytesLeft+=sizeChunks;
+ }
+ int numRead=in.read(buf);
+ bytesLeft-=numRead;
+ if (moved(numRead))
+ {
+ socket.close();
+ return;
+ }
+ }
+ }
+ catch (Throwable th)
+ {
+ setError(th);
+ }
+ finally
+ {
+ try { socket.close(); } catch(Throwable th) {}
+ }
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/FullSpeedTest.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/FullSpeedTest.java
new file mode 100644
index 0000000..94a9028
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/FullSpeedTest.java
@@ -0,0 +1,102 @@
+/**
+ *
+ * This example is released under the MIT license, so it can be copied, modified and used in
+ * proprietary software.
+ *
+ * Note the the speed test core library is released under LGLP. PAY ATTENTION.
+ *
+ */
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+
+import javax.net.SocketFactory;
+
+public class FullSpeedTest
+{
+ UploadSpeedTest uploadSpeedTest;
+ DownloadSpeedTest downloadSpeedTest;
+ PingTest pingTest;
+ GetIP getIP;
+ TestPoint tp;
+
+ boolean running = false;
+ boolean ended;
+ SpeedTestResult res;
+ public FullSpeedTest()
+ {
+ uploadSpeedTest = new UploadSpeedTest();
+ downloadSpeedTest = new DownloadSpeedTest();
+ pingTest = new PingTest();
+ getIP = new GetIP();
+ }
+ class DeathListener implements SpeedTestListener
+ {
+ @Override
+ public void speedTestEnded()
+ {
+ }
+ }
+ public SpeedTestResult getResult()
+ {
+ GetIP.IPResult ipRes = getIP.getResult();
+ PingTest.PingResult pingRes = pingTest.getResult();
+ DownUpSpeedTest.DownUpSpeedResult downRes = downloadSpeedTest.getResult();
+ DownUpSpeedTest.DownUpSpeedResult upRes = uploadSpeedTest.getResult();
+ SpeedTestResult res =new SpeedTestResult(tp);
+ res.ping = pingRes.ping;
+ res.jitter = pingRes.jitter;
+ res.downloadSpeed = downRes.speed;
+ res.uploadSpeed = upRes.speed;
+ res.ipInfo = ipRes.ipData;
+ if (res.error==null) res.error = ipRes.error;
+ if (res.error==null) res.error = pingRes.error;
+ if (res.error==null) res.error = downRes.error;
+ if (res.error==null) res.error = upRes.error;
+ res.progress_ping = pingRes.percentage;
+ res.progress_jitter = pingRes.percentage;
+ res.progress_downloadSpeed = downRes.percentage;
+ res.progress_uploadSpeed = upRes.percentage;
+ synchronized (this)
+ {
+ res.ended = ended;
+ }
+ return res;
+ }
+ public void test(TestPoint tp, SpeedTestListener listener,SocketFactory clientSocketFactory)
+ {
+ DeathListener deathListener = new DeathListener();
+ synchronized(this)
+ {
+ if (running)
+ return;
+ running = true;
+ ended = false;
+ this.tp = tp;
+ res = new SpeedTestResult(tp);
+ }
+ try
+ {
+ getIP.clear();
+ pingTest.clear();
+ downloadSpeedTest.clear();
+ uploadSpeedTest.clear();
+ getIP.test(tp.getHost(), tp.getPort(), tp.getGetIPPath(), deathListener, clientSocketFactory);
+ pingTest.test(tp.getHost(), tp.getPort(), tp.getPingPath(), deathListener, clientSocketFactory);
+ downloadSpeedTest.test(tp.getHost(), tp.getPort(), tp.getDownloadPath(), 10, deathListener, clientSocketFactory);
+ uploadSpeedTest.test(tp.getHost(), tp.getPort(), tp.getUploadPath(), 10, deathListener, clientSocketFactory);
+ }
+ catch (Throwable e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ synchronized (this)
+ {
+ running = false;
+ ended = true;
+ }
+ }
+ listener.speedTestEnded();
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/GetIP.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/GetIP.java
new file mode 100644
index 0000000..03b86f1
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/GetIP.java
@@ -0,0 +1,144 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.HashMap;
+
+import javax.net.SocketFactory;
+
+public class GetIP
+{
+ String ipData;
+ Throwable error;
+ boolean running;
+ boolean ended;
+
+ synchronized void update(String ipData)
+ {
+ this.ipData = ipData;
+ }
+ synchronized void setError(Throwable error)
+ {
+ this.error = error;
+ }
+
+ static class IPResult
+ {
+ boolean ended;
+ double percentage;
+ String ipData;
+ Throwable error;
+ public IPResult(boolean ended, double percentage, String ipData, Throwable error)
+ {
+ this.ended = ended;
+ this.percentage = percentage;
+ this.ipData = ipData;
+ this.error = error;
+ }
+ public String toString()
+ {
+ return "ended = " + ended +
+ " percentage = " + percentage +
+ " idData = " + ipData;
+ }
+ }
+ public synchronized IPResult getResult()
+ {
+ return new IPResult(ended, ended?100:0, ipData, error);
+ }
+
+ public synchronized void clear()
+ {
+ ipData = null;
+ error = null;
+ ended = false;
+ }
+ public void test(String host, int port, String path, SpeedTestListener log, SocketFactory clientSocketFactory)
+ {
+ synchronized (this)
+ {
+ if (running)
+ return; // does not not start again if still running
+ running = true;
+ }
+ clear();
+ try
+ {
+ SocketFactory factory = clientSocketFactory==null ? SocketFactory.getDefault() : clientSocketFactory;
+ Socket socket = factory.createSocket();
+ socket.connect(new InetSocketAddress(host, port));
+ testIntern(socket, host, port, path);
+ }
+ catch(Throwable th)
+ {
+ setError(th);
+ }
+ finally
+ {
+ synchronized (this)
+ {
+ running = false;
+ ended = true;
+ }
+ log.speedTestEnded();
+ }
+ }
+
+ void testIntern(Socket socket, String host, int port, String path) throws IOException
+ {
+ OutputStream out = socket.getOutputStream();
+ InputStream in = socket.getInputStream();
+ try
+ {
+ path += "?isp=true&distance=km";
+ HTTPHelper.putGetHeadersInStream(out, host, path);
+ HTTPHelper.HTTPLineReader lineReader = new HTTPHelper.HTTPLineReader(in);
+ boolean chunked=false;
+ boolean ok=false;
+ int content_length = 0;
+ for(;;) // reads HTTP headers sent by the server
+ {
+ String l = lineReader.readLine();
+ if(l==null) break;
+ l = l.trim().toLowerCase();
+ if (l.equals("transfer-encoding: chunked")) chunked=true;
+ if (l.contains("200 ok")) ok=true;
+ if (l.contains("content-length"))
+ content_length = Integer.parseInt(l.split(":")[1].trim());
+ if (l.trim().isEmpty())
+ break;
+ }
+ if(!chunked)
+ {
+ // read all at once
+ BufferedReader br=new BufferedReader(new InputStreamReader(in));
+ char[] buf=new char[content_length];
+ br.read(buf);
+ String data=new String(buf);
+ update(data);
+ }
+ else
+ {
+ // the server does not follow the official protocol for chunked content
+ lineReader.readLine();
+ String l = lineReader.readLine();
+ update(l);
+ lineReader.readLine();
+ }
+ }
+ catch(Throwable th)
+ {
+ setError(th);
+ }
+ finally
+ {
+ try { socket.close(); } catch(Throwable th) {}
+ }
+
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/HTTPHelper.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/HTTPHelper.java
new file mode 100644
index 0000000..219d361
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/HTTPHelper.java
@@ -0,0 +1,54 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+
+public class HTTPHelper
+{
+ static void putPostHeadersInStream(OutputStream out, String host, String path, int length) throws UnsupportedEncodingException
+ {
+ PrintStream ps = new PrintStream(out, false, "utf-8");
+ ps.print("POST "+path+" HTTP/1.1\r\n"); // /backend/empty.php
+ ps.print("Host: "+host+"\r\n"); // 192.168.0.102:8080
+ ps.print("Connection: keep-alive\r\n");
+ ps.print("Accept-Encoding: identity\r\n");
+ ps.print("Content-Type: application/octet-stream\r\n");
+ ps.print("Content-Length: " + length + "\r\n");
+ ps.print("\r\n");
+ ps.flush();
+ }
+ static void putGetHeadersInStream(OutputStream out, String host, String path) throws UnsupportedEncodingException
+ {
+ PrintStream ps = new PrintStream(out, false, "utf-8");
+ ps.print("GET "+path+" HTTP/1.1\r\n");
+ ps.print("Host: "+host+"\r\n");
+ ps.print("Connection: keep-alive\r\n");
+ ps.print("Accept-Encoding: identity\r\n");
+ ps.print("\r\n");
+ ps.flush();
+ }
+ public static class HTTPLineReader
+ {
+ InputStreamReader isr;
+ HTTPLineReader(InputStream in) throws UnsupportedEncodingException
+ {
+ isr = new InputStreamReader(in, "utf-8");
+ }
+ String readLine() throws IOException
+ {
+ StringBuilder sb = new StringBuilder();
+ while (true)
+ {
+ int c = isr.read();
+ if (c == -1) break;
+ sb.append((char) c);
+ if (c == '\n') break;
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/MainActivity.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/MainActivity.java
new file mode 100644
index 0000000..4db852a
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/MainActivity.java
@@ -0,0 +1,104 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.method.ScrollingMovementMethod;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class MainActivity extends AppCompatActivity implements SpeedTestListener
+{
+ Button button_run;
+ TextView tvReport;
+ UploadSpeedTest uploadSpeedTest;
+ DownloadSpeedTest downloadSpeedTest;
+ PingTest pingTest;
+ GetIP getIP;
+ FullSpeedTest fullSpeedTest;
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ uploadSpeedTest = new UploadSpeedTest();
+ downloadSpeedTest = new DownloadSpeedTest();
+ pingTest = new PingTest();
+ getIP = new GetIP();
+ fullSpeedTest = new FullSpeedTest();
+ setContentView(R.layout.activity_main);
+ button_run = ((Button)findViewById(R.id.button_run));
+ tvReport = ((TextView)findViewById(R.id.tv_report));
+ tvReport.setMovementMethod(new ScrollingMovementMethod());
+ button_run.setOnClickListener
+ (
+ new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View view)
+ {
+ new Thread(new TaskSpeed()).start();
+ }
+ }
+ );
+
+ }
+ public void speedTestLog(final String s)
+ {
+
+ final Handler UIHandler = new Handler(Looper.getMainLooper());
+ UIHandler .post
+ (
+ new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ tvReport.append(s+"\n");
+ System.out.println(s);
+ }
+ }
+ );
+ }
+
+ @Override
+ public void speedTestEnded()
+ {
+ SpeedTestResult res = fullSpeedTest.getResult();
+ speedTestLog(res.toString());
+ }
+
+ private class TaskSpeed implements Runnable
+ {
+ @Override
+ public void run()
+ {
+
+ String name = "Helsinki, Finland";
+ String server = "//fi.openspeed.org";
+ String downLoadPath = "garbage.php";
+ String uploadPath = "empty.php";
+ String pingPath = "empty.php";
+ String getpath = "getIP.php";
+
+ /*
+ String name = "PlayIP";
+ String server = "http://170.238.84.8:8080/";
+ String downLoadPath = "/backend/garbage.php";
+ String uploadPath = "/backend/empty.php";
+ String pingPath = "/backend/empty.php";
+ String getpath = "/backend/getIP.php";
+*/
+
+ TestPoint tp = new TestPoint(name, server, downLoadPath, uploadPath, pingPath, getpath);
+ fullSpeedTest.test(tp, MainActivity.this, null);
+ SpeedTestResult res = fullSpeedTest.getResult();
+ speedTestLog(res.toString());
+
+ }
+ }
+
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/PingTest.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/PingTest.java
new file mode 100644
index 0000000..5951f0c
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/PingTest.java
@@ -0,0 +1,163 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import javax.net.SocketFactory;
+
+public class PingTest
+{
+ double jitter;
+ int counter;
+ double minPing;
+ double prevPing;
+ boolean running;
+ Throwable error;
+ boolean ended;
+
+ int numToPing = 10;
+
+ synchronized boolean update(long ns)
+ {
+ counter++;
+ double ms = ns / 1000000.0;
+ if (ms < minPing) minPing = ms;
+ //ping = minPing;
+ if (prevPing == -1)
+ jitter=0;
+ else
+ {
+ double j = Math.abs(ms - prevPing);
+ jitter=j>jitter?(jitter*0.3+j*0.7):(jitter*0.8+j*0.2);
+ }
+ prevPing = ms;
+ return counter > numToPing;
+ }
+ synchronized void setError(Throwable error)
+ {
+ this.error = error;
+ }
+
+ static class PingResult
+ {
+ boolean ended;
+ double percentage;
+ double ping;
+ double jitter;
+ Throwable error;
+ public PingResult(boolean ended, double percentage, double ping, double jitter, Throwable th)
+ {
+ this.ended = ended;
+ this.percentage = percentage;
+ this.ping = ping;
+ this.jitter = jitter;
+ this.error = error;
+ }
+ public String toString()
+ {
+ return "ended = " + ended +
+ " percentage = " + percentage +
+ " ping = " + ping +
+ " jitter = " + jitter;
+ }
+ }
+ public synchronized PingResult getResult()
+ {
+ if (!ended && !running)
+ new PingResult(false, 0, 0, 0, error);
+ return new PingResult(ended, counter * 100.0 / numToPing, minPing, jitter, error);
+ }
+ public synchronized void clear()
+ {
+ prevPing = -1;
+ minPing = Double.MAX_VALUE;
+ counter = 0;
+ error = null;
+ ended = false;
+ }
+ public void test(String host, int port, String path, SpeedTestListener log, SocketFactory clientSocketFactory)
+ {
+ synchronized (this)
+ {
+
+ if (running)
+ return; // does not not start again if still running
+ running = true;
+ clear();
+ }
+ try
+ {
+ SocketFactory factory = clientSocketFactory==null ? SocketFactory.getDefault() : clientSocketFactory;
+ Socket socket = factory.createSocket();
+ socket.connect(new InetSocketAddress(host, port));
+ testIntern(socket, host, port, path);
+ }
+ catch(Throwable th)
+ {
+ setError(th);
+ }
+ finally
+ {
+ synchronized (this)
+ {
+ running = false;
+ ended = true;
+ }
+ log.speedTestEnded();
+ }
+ }
+
+ void testIntern(Socket socket, String host, int port, String path) throws IOException
+ {
+
+ OutputStream out = socket.getOutputStream();
+ InputStream in = socket.getInputStream();
+ try
+ {
+ String s=path;
+ for(;;)
+ {
+ HTTPHelper.putGetHeadersInStream(out, host, path);
+ long t=System.nanoTime();
+ boolean chunked=false;
+ boolean ok=false;
+ HTTPHelper.HTTPLineReader lineReader = new HTTPHelper.HTTPLineReader(in);
+ for(;;) // reads HTTP headers sent by the server
+ {
+ String l = lineReader.readLine();
+ if(l==null) break;
+ l = l.trim().toLowerCase();
+ if (l.equals("transfer-encoding: chunked")) chunked=true;
+ if (l.contains("200 ok")) ok=true;
+ if (l.trim().isEmpty())
+ {
+ if(chunked)
+ {
+ lineReader.readLine();
+ lineReader.readLine();
+ }
+ break;
+ }
+ }
+ if(!ok)
+ throw new Exception("Did not get a 200");
+ t=System.nanoTime()-t;
+ if(update(t/2))
+ break;
+ }
+ }
+ catch(Throwable th)
+ {
+ setError(th);
+ }
+ finally
+ {
+ try { socket.close(); } catch(Throwable th) {}
+ }
+
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/SpeedTestListener.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/SpeedTestListener.java
new file mode 100644
index 0000000..e090d6d
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/SpeedTestListener.java
@@ -0,0 +1,7 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+public interface SpeedTestListener
+{
+ //void speedTestProgress(); // if desired, the listener can ask for the current test results in this opportunity
+ void speedTestEnded();
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/SpeedTestResult.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/SpeedTestResult.java
new file mode 100644
index 0000000..c9d278e
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/SpeedTestResult.java
@@ -0,0 +1,54 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+class SpeedTestResult
+{
+ double ping;
+ double jitter;
+ double downloadSpeed;
+ double uploadSpeed;
+ String ipInfo;
+ Throwable error;
+ double progress_ping;
+ double progress_jitter;
+ double progress_downloadSpeed;
+ double progress_uploadSpeed;
+ boolean ended;
+ TestPoint tp;
+
+ SpeedTestResult(TestPoint tp)
+ {
+ this.tp = tp;
+ }
+ public synchronized String toString()
+ {
+ return toJSON().toString();
+ }
+ public synchronized JSONObject toJSON()
+ {
+ try
+ {
+ JSONObject res = new JSONObject();
+ res.put("ping", ping);
+ res.put("jitter", jitter);
+ res.put("downloadSpeed",downloadSpeed);
+ res.put("uploadSpeed",uploadSpeed);
+ res.put("ipInfo",ipInfo);
+ if (error!=null)
+ res.put("error",error.getMessage());
+ res.put("progress_ping", progress_ping);
+ res.put("progress_jitter", progress_jitter);
+ res.put("progress_downloadSpeed",progress_downloadSpeed);
+ res.put("progress_uploadSpeed",progress_uploadSpeed);
+ res.put("ended",ended);
+ res.put("server",tp.toJSON());
+ return res;
+ }
+ catch (JSONException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/TestPoint.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/TestPoint.java
new file mode 100644
index 0000000..690eed9
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/TestPoint.java
@@ -0,0 +1,119 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.URL;
+
+public class TestPoint
+{
+ private final String name;
+ private final String server; // include the port, if needed, after a ":"
+ private final String downloadPath;
+ private final String uploadPath;
+ private final String pingPath;
+ private final String getIPPath;
+
+ protected float ping=-1;
+
+ public TestPoint(String name, String server, String downloadPath, String uploadPath, String pingPath, String getIPPath)
+ {
+ this.name=name;
+ this.server=server;
+ this.downloadPath = downloadPath;
+ this.uploadPath = uploadPath;
+ this.pingPath = pingPath;
+ this.getIPPath = getIPPath;
+ }
+
+ public int getPort()
+ {
+ try
+ {
+ URL u=new URL(server);
+ return u.getPort();
+ }
+ catch(Throwable t)
+ {
+ throw new IllegalArgumentException("Malformed URL (HTTP)");
+ }
+ }
+ public String getHost()
+ {
+ try
+ {
+ URL u=new URL(server);
+ return u.getHost();
+ }
+ catch(Throwable t)
+ {
+ throw new IllegalArgumentException("Malformed URL (HTTP)");
+ }
+ }
+
+ public TestPoint(JSONObject json){
+ try {
+ name = json.getString("name");
+ if (name == null) throw new IllegalArgumentException("Missing name field");
+ server = json.getString("server");
+ if (server == null) throw new IllegalArgumentException("Missing server field");
+ downloadPath = json.getString("dlURL");
+ if (downloadPath == null) throw new IllegalArgumentException("Missing dlURL field");
+ uploadPath = json.getString("ulURL");
+ if (uploadPath == null) throw new IllegalArgumentException("Missing ulURL field");
+ pingPath = json.getString("pingURL");
+ if (pingPath == null) throw new IllegalArgumentException("Missing pingURL field");
+ getIPPath = json.getString("getIpURL");
+ if (getIPPath == null) throw new IllegalArgumentException("Missing getIpURL field");
+ }catch (JSONException t){
+ throw new IllegalArgumentException("Invalid JSON");
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public String getDownloadPath() {
+ return downloadPath;
+ }
+
+ public String getUploadPath() {
+ return uploadPath;
+ }
+
+ public String getPingPath() {
+ return pingPath;
+ }
+
+ public String getGetIPPath() {
+ return getIPPath;
+ }
+
+ public float getPing() {
+ return ping;
+ }
+
+ public JSONObject toJSON()
+ {
+ JSONObject json = new JSONObject();
+ try
+ {
+ json.put("name", name);
+ json.put("server", server);
+ json.put("dlURL", downloadPath);
+ json.put("ulURL", uploadPath);
+ json.put("pingURL", pingPath);
+ json.put("getIpURL", getIPPath);
+ return json;
+ }
+ catch (JSONException e)
+ {
+ return json;
+ }
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/UploadSpeedTest.java b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/UploadSpeedTest.java
new file mode 100644
index 0000000..17323c2
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/java/com/fdossena/speedtest/httpverybasicspeedtest/UploadSpeedTest.java
@@ -0,0 +1,94 @@
+/**
+ *
+ * This example is released under the MIT license, so it can be copied, modified and used in
+ * proprietary software.
+ *
+ * Note the the speed test core library is released under LGLP. PAY ATTENTION.
+ *
+ */
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import javax.net.SocketFactory;
+
+public class UploadSpeedTest extends DownUpSpeedTest
+{
+ byte[] dataField;
+
+
+ public UploadSpeedTest()
+ {
+ dataField = new byte[lenData]; //
+ int v = 13;
+ dataField[0] = (byte) (v % 256);
+ for (int t = 0; t < dataField.length; t++)
+ {
+ v = v * 17 + t * 31 + 7;
+ dataField[t] = (byte) (v % 256);
+ }
+
+ }
+
+ class Uploader extends SocketTestRunnable
+ {
+ public Uploader(SpeedTestListener log, String host, int port, String path, Socket socket) throws IOException
+ {
+ super(log, host, port, path, socket);
+ }
+ public void run()
+ {
+ testUploadIntern(socket, host, port, path);
+ }
+ }
+ SocketTestRunnable getSocketTestRunnable(SpeedTestListener log, String host, int port, String path, Socket socket) throws IOException
+ {
+ return new Uploader(log, host, port, path, socket);
+ }
+
+ int n = 3;
+ int lenData = 2*1048576;
+
+ private void testUploadIntern(Socket socket, String host, int port, String path)
+ {
+ try
+ {
+ byte[] data = dataField;
+ OutputStream out = socket.getOutputStream();
+ InputStream in = socket.getInputStream();
+ for (;;)
+ {
+ HTTPHelper.putPostHeadersInStream(out, host, path, data.length * n);
+ int bufSize = 64 * 1024;
+ for (int tt = 0; tt < n; tt++)
+ {
+ for (int i=0; i < lenData / bufSize; i++)
+ {
+ out.write(data, i * bufSize, bufSize);
+ if (moved(bufSize))
+ return;
+ }
+ }
+ HTTPHelper.HTTPLineReader lineReader = new HTTPHelper.HTTPLineReader(in);
+ for (;;) // reads and ignores the server answer, which consists of http headers
+ {
+ String lin = lineReader.readLine();
+ if (lin == null || lin.trim().isEmpty())
+ break;
+ }
+ }
+ }
+ catch (Throwable th)
+ {
+ setError(th);
+ }
+ finally
+ {
+ try { socket.close(); } catch(Throwable th) {}
+ }
+ }
+}
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/drawable/ic_launcher_background.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/layout/activity_main.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..c4973bd
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-hdpi/ic_launcher.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-mdpi/ic_launcher.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xhdpi/ic_launcher.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/Speedtest-Android/httpverybasicspeedtest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/colors.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/colors.xml
new file mode 100644
index 0000000..4faecfa
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #6200EE
+ #3700B3
+ #03DAC5
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/strings.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/strings.xml
new file mode 100644
index 0000000..3300d38
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ HTTPVeryBasicSpeedTest
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/styles.xml b/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/styles.xml
new file mode 100644
index 0000000..fac9291
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speedtest-Android/httpverybasicspeedtest/src/test/java/com/fdossena/speedtest/httpverybasicspeedtest/ExampleUnitTest.java b/Speedtest-Android/httpverybasicspeedtest/src/test/java/com/fdossena/speedtest/httpverybasicspeedtest/ExampleUnitTest.java
new file mode 100644
index 0000000..b4a0c8b
--- /dev/null
+++ b/Speedtest-Android/httpverybasicspeedtest/src/test/java/com/fdossena/speedtest/httpverybasicspeedtest/ExampleUnitTest.java
@@ -0,0 +1,19 @@
+package com.fdossena.speedtest.httpverybasicspeedtest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest
+{
+ @Test
+ public void addition_isCorrect()
+ {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Speedtest-Android/settings.gradle b/Speedtest-Android/settings.gradle
index 33b9b25..970a40c 100644
--- a/Speedtest-Android/settings.gradle
+++ b/Speedtest-Android/settings.gradle
@@ -1,2 +1,5 @@
+include ':httpverybasicspeedtest'
+include ':examplespeedtest'
+include ':core'
include ':app'
-rootProject.name='Speedtest-Android'
+rootProject.name = "speedtest-android-lib"
\ No newline at end of file