/*******************************************************************************
* Copyright 2013-2016 alladin-IT GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package at.alladin.rmbt.android.test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.util.Log;
import at.alladin.rmbt.android.impl.TracerouteAndroidImpl;
import at.alladin.rmbt.android.util.Config;
import at.alladin.rmbt.android.util.ConfigHelper;
import at.alladin.rmbt.android.util.InformationCollector;
import at.alladin.rmbt.client.QualityOfServiceTest;
import at.alladin.rmbt.client.QualityOfServiceTest.Counter;
import at.alladin.rmbt.client.RMBTClient;
import at.alladin.rmbt.client.TestResult;
import at.alladin.rmbt.client.helper.ControlServerConnection;
import at.alladin.rmbt.client.helper.IntermediateResult;
import at.alladin.rmbt.client.helper.NdtStatus;
import at.alladin.rmbt.client.helper.TestStatus;
import at.alladin.rmbt.client.ndt.NDTRunner;
import at.alladin.rmbt.client.v2.task.QoSTestEnum;
import at.alladin.rmbt.client.v2.task.result.QoSResultCollector;
import at.alladin.rmbt.client.v2.task.result.QoSTestResultEnum;
import at.alladin.rmbt.client.v2.task.service.TestMeasurement;
import at.alladin.rmbt.client.v2.task.service.TestSettings;
import at.alladin.rmbt.client.v2.task.service.TrafficService;
import at.alladin.rmbt.util.model.shared.exception.ErrorStatus;
import net.measurementlab.ndt.NdtTests;
public class RMBTTask
{
private static final String LOG_TAG = "RMBTTask";
public static enum RMBTTaskError {
NONE,
CONNECTION_ERROR,
SPEEDTEST_ERROR,
QOS_ERROR,
OTHER
}
public static final String BROADCAST_TEST_REQUEST = "at.alladin.rmbt.android.test.RMBTTask.testRequest";
public static final String BROADCAST_TEST_START = "at.alladin.rmbt.android.test.RMBTTask.testStart";
private final AtomicBoolean started = new AtomicBoolean();
private final AtomicBoolean running = new AtomicBoolean();
private final AtomicBoolean finished = new AtomicBoolean();
private final AtomicBoolean cancelled = new AtomicBoolean();
private final AtomicReference<RMBTTaskError> error = new AtomicReference<RMBTTask.RMBTTaskError>(RMBTTaskError.NONE);
private final AtomicReference<QualityOfServiceTest> qosReference = new AtomicReference<QualityOfServiceTest>();
private Handler handler;
private final Runnable postExecuteHandler = new Runnable()
{
@Override
public void run()
{
if (fullInfo != null)
{
fullInfo.unload();
fullInfo = null;
}
if (endTaskListener != null)
endTaskListener.taskEnded();
}
};;
final private Context context;
private final ExecutorService executor = Executors.newCachedThreadPool();
private final AtomicBoolean connectionError = new AtomicBoolean();
private final AtomicReference<Set<ErrorStatus>> errorStatusList = new AtomicReference<Set<ErrorStatus>>();
private RMBTClient client;
private InformationCollector fullInfo;
private EndTaskListener endTaskListener;
interface EndTaskListener
{
public void taskEnded(final int...flag);
}
public RMBTTask(final Context ctx)
{
this.context = ctx;
}
public void execute(final Handler _handler)
{
fullInfo = new InformationCollector(context, true, true);
cancelled.set(false);
started.set(true);
running.set(true);
finished.set(false);
handler = _handler;
executor.execute(new Runnable()
{
@Override
public void run()
{
Log.d(LOG_TAG, "executor task started");
doInBackground();
Log.d(LOG_TAG, "doInBackground finished");
running.set(false);
finished.set(true);
if (handler != null)
handler.post(postExecuteHandler);
Log.d(LOG_TAG, "executor task finished");
}
});
}
public void cancel()
{
setPreviousTestStatus();
cancelled.set(true);
executor.shutdownNow();
Log.d(LOG_TAG, "shutdownNow called RMBTTask="+this);
// try
// {
// executor.awaitTermination(10, TimeUnit.SECONDS);
// }
// catch (InterruptedException e)
// {
// Thread.currentThread().interrupt();
// }
}
public boolean isFinished()
{
return finished.get();
}
public boolean isRunning()
{
return running.get() && ! cancelled.get();
}
private void setPreviousTestStatus()
{
final TestStatus status;
if (client == null)
status = null;
else
status = client.getStatus();
final String statusString;
if (status == TestStatus.ERROR)
{
final TestStatus statusBeforeError = client.getStatusBeforeError();
if (statusBeforeError != null)
statusString = "ERROR_" + statusBeforeError.toString();
else
statusString = "ERROR";
}
else if (status != null)
statusString = status.toString();
else
statusString = null;
System.out.println("test status at end: " + statusString);
ConfigHelper.setPreviousTestStatus(context, statusString);
}
private void doInBackground()
{
try
{
context.sendBroadcast(new Intent(BROADCAST_TEST_REQUEST));
boolean hasError = false;
connectionError.set(false);
TestResult result = null;
QoSResultCollector qosResult = null;
try
{
final String uuid = fullInfo.getUUID();
final String controlServer = ConfigHelper.getControlServerName(context);
final int controlPort = ConfigHelper.getControlServerPort(context);
final boolean controlSSL = ConfigHelper.isControlSeverSSL(context);
final ArrayList<String> geoInfo = fullInfo.getCurLocation();
final Set<ErrorStatus> errorSet = new HashSet<ErrorStatus>();
client = RMBTClient.getInstance(controlServer, null, controlPort, controlSSL, geoInfo, uuid,
Config.RMBT_CLIENT_TYPE, Config.RMBT_CLIENT_NAME,
fullInfo.getInfo("CLIENT_SOFTWARE_VERSION"), null, fullInfo.getInitialInfo(), errorSet);
if (errorSet.size() > 0) {
System.out.println(errorSet);
errorStatusList.set(errorSet);
}
if (client != null)
{
client.setTrafficService(new TrafficServiceImpl());
final ControlServerConnection controlConnection = client.getControlConnection();
if (controlConnection != null)
{
fullInfo.setUUID(controlConnection.getClientUUID());
fullInfo.setTestServerName(controlConnection.getServerName());
}
}
}
catch (final Exception e)
{
e.printStackTrace();
hasError = true;
}
if (hasError || client == null) {
error.set(RMBTTaskError.CONNECTION_ERROR);
connectionError.set(true);
}
else
{
if (client.getStatus() != TestStatus.ERROR)
{
try
{
if (Thread.interrupted() || cancelled.get())
throw new InterruptedException();
Log.d(LOG_TAG, "runTest RMBTTask="+this);
context.sendBroadcast(new Intent(BROADCAST_TEST_START));
result = client.runTest();
final ControlServerConnection controlConnection = client.getControlConnection();
if (result != null && ! fullInfo.getIllegalNetworkTypeChangeDetcted()) {
client.sendResult(fullInfo.getResultValues(controlConnection.getStartTimeNs()));
}
else {
hasError = true;
}
}
catch (final Exception e)
{
e.printStackTrace();
}
finally
{
client.shutdown();
}
}
else
{
System.err.println(client.getErrorMsg());
hasError = true;
}
if (hasError) {
error.set(RMBTTaskError.SPEEDTEST_ERROR);
}
//client.shutdown();
setPreviousTestStatus();
QualityOfServiceTest qosTest = null;
boolean runQoS = (! ConfigHelper.isSkipQoS(context) && client.getTaskDescList() != null && client.getTaskDescList().size() >= 1);
//run qos test:
if (runQoS && !hasError && !cancelled.get()) {
try {
TestSettings qosTestSettings = new TestSettings();
qosTestSettings.setCacheFolder(context.getCacheDir());
qosTestSettings.setWebsiteTestService(new WebsiteTestServiceImpl(context));
qosTestSettings.setTracerouteServiceClazz(TracerouteAndroidImpl.class);
qosTestSettings.setTrafficService(new TrafficServiceImpl());
qosTestSettings.setStartTimeNs(getRmbtClient().getControlConnection().getStartTimeNs());
qosTestSettings.setUseSsl(ConfigHelper.isQoSSeverSSL(context));
qosTest = new QualityOfServiceTest(client, qosTestSettings);
qosReference.set(qosTest);
client.setStatus(TestStatus.QOS_TEST_RUNNING);
qosResult = qosTest.call();
InformationCollector.qoSResult = qosResult;
if (!cancelled.get()) {
if (qosResult != null && !qosTest.getStatus().equals(QoSTestEnum.ERROR)) {
client.sendQoSResult(qosResult);
}
}
} catch (Exception e) {
e.printStackTrace();
hasError = true;
error.set(RMBTTaskError.QOS_ERROR);
}
}
if (qosTest != null && !cancelled.get() && qosTest.getStatus().equals(QoSTestEnum.QOS_FINISHED)) {
if (ConfigHelper.isNDT(context)) {
qosTest.setStatus(QoSTestEnum.NDT_RUNNING);
runNDT();
}
qosTest.setStatus(QoSTestEnum.STOP);
}
}
}
catch (final Exception e)
{
client.setStatus(TestStatus.ERROR);
e.printStackTrace();
Thread.currentThread().interrupt();
}
finally
{
try
{
if (client != null)
{
final TestStatus status = client.getStatus();
if (! (status == TestStatus.ABORTED || status == TestStatus.ERROR))
{
client.setStatus(TestStatus.END);
}
else {
error.set(RMBTTaskError.OTHER);
}
}
}
catch (Exception e)
{}
}
}
private final AtomicReference<NDTRunner> ndtRunnerHolder = new AtomicReference<NDTRunner>();
public float getNDTProgress()
{
final NDTRunner ndtRunner = ndtRunnerHolder.get();
if (ndtRunner == null)
return 0;
return ndtRunner.getNdtProgress();
}
public NdtStatus getNdtStatus()
{
final NDTRunner ndtRunner = ndtRunnerHolder.get();
if (ndtRunner == null)
return null;
return ndtRunner.getNdtStatus();
}
public void stopNDT()
{
final NDTRunner ndtRunner = ndtRunnerHolder.get();
if (ndtRunner != null)
ndtRunner.setNdtCacelled(true);
}
public void runNDT()
{
final NDTRunner ndtRunner = new NDTRunner();
ndtRunnerHolder.set(ndtRunner);
Log.d(LOG_TAG, "ndt status RUNNING");
final String ndtNetworkType;
final int networkType = getNetworkType();
switch (networkType)
{
case InformationCollector.NETWORK_WIFI:
ndtNetworkType = NdtTests.NETWORK_WIFI;
break;
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
ndtNetworkType = NdtTests.NETWORK_UNKNOWN;
break;
default:
ndtNetworkType = NdtTests.NETWORK_MOBILE;
break;
}
ndtRunner.runNDT(ndtNetworkType, ndtRunner.new UiServices()
{
@Override
public void sendResults()
{
client.getControlConnection().sendNDTResult(this, null);
}
public boolean wantToStop()
{
if (super.wantToStop())
return true;
if (cancelled.get())
{
cancel();
return true;
}
return false;
}
});
}
/**
*
* @return
*/
public float getQoSTestProgress()
{
final QualityOfServiceTest nnTest = qosReference.get();
if (nnTest == null)
return 0;
return nnTest.getProgress();
}
/**
*
* @return
*/
public int getQoSTestSize() {
final QualityOfServiceTest nnTest = qosReference.get();
if (nnTest == null)
return 0;
return nnTest.getTestSize();
}
/**
*
* @return
*/
public QualityOfServiceTest getQoSTest() {
return qosReference.get();
}
/**
*
* @return
*/
public QoSTestEnum getQoSTestStatus()
{
final QualityOfServiceTest nnTest = qosReference.get();
if (nnTest == null)
return null;
return nnTest.getStatus();
}
/**
*
* @return
*/
public Map<QoSTestResultEnum, Counter> getQoSGroupCounterMap() {
final QualityOfServiceTest nnTest = qosReference.get();
if (nnTest == null)
return null;
return nnTest.getTestGroupCounterMap();
}
public void setEndTaskListener(final EndTaskListener endTaskListener)
{
this.endTaskListener = endTaskListener;
}
public Integer getSignal()
{
if (fullInfo != null)
return fullInfo.getSignal();
else
return null;
}
public int getSignalType()
{
if (fullInfo != null)
return fullInfo.getSignalType();
else
return InformationCollector.SINGAL_TYPE_NO_SIGNAL;
}
public IntermediateResult getIntermediateResult(final IntermediateResult result)
{
if (client == null)
return null;
return client.getIntermediateResult(result);
}
public boolean isConnectionError()
{
return connectionError.get();
}
public boolean isCancelled() {
return cancelled.get();
}
public RMBTTaskError getError() {
return error.get();
}
public String getOperatorName()
{
if (fullInfo != null)
return fullInfo.getOperatorName();
else
return null;
}
public Location getLocation()
{
if (fullInfo != null)
return fullInfo.getLastLocation();
else
return null;
}
public String getServerName()
{
if (fullInfo != null)
return fullInfo.getTestServerName();
else
return null;
}
public String getIP()
{
if (client != null)
return client.getPublicIP();
else
return null;
}
public String getTestUuid()
{
if (cancelled.get() || connectionError.get())
return null;
if (client != null)
return client.getTestUuid();
else
return null;
}
public int getNetworkType()
{
if (fullInfo != null)
{
final int networkType = fullInfo.getNetwork();
if (fullInfo.getIllegalNetworkTypeChangeDetcted())
{
Log.e(LOG_TAG, "illegal network change detected; cancelling test");
cancel();
}
return networkType;
}
else
return 0;
}
public long getStartTimeMillis() {
return client != null ? client.getStartTimeMillis() : 0;
}
public RMBTClient getRmbtClient() {
return client;
}
public TrafficService getSpeedTestTrafficService() {
if (client != null) {
return client.getTrafficService();
}
return null;
}
public TrafficService getQoSTrafficService() {
if (qosReference.get() != null) {
return qosReference.get().getTestSettings().getTrafficService();
}
return null;
}
public Map<TestStatus, TestMeasurement> getTrafficMeasurementMap() {
if (client != null) return client.getTrafficMeasurementMap();
return null;
}
public InformationCollector getInformationCollector() {
return fullInfo;
}
public Set<ErrorStatus> getErrorStatusList() {
return errorStatusList.get();
}
}