at.alladin.rmbt.android.test.RMBTLoopService.java Source code

Java tutorial

Introduction

Here is the source code for at.alladin.rmbt.android.test.RMBTLoopService.java

Source

/*******************************************************************************
 * 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.text.MessageFormat;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.json.JSONArray;
import org.json.JSONException;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.res.Resources;
import android.location.Location;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
import at.alladin.openrmbt.android.R;
import at.alladin.rmbt.android.loopmode.LoopModeCurrentTest;
import at.alladin.rmbt.android.loopmode.LoopModeLastTestResults;
import at.alladin.rmbt.android.loopmode.LoopModeLastTestResults.RMBTLoopFetchingResultsStatus;
import at.alladin.rmbt.android.loopmode.LoopModeLastTestResults.RMBTLoopLastTestStatus;
import at.alladin.rmbt.android.loopmode.LoopModeResults;
import at.alladin.rmbt.android.loopmode.LoopModeResults.Status;
import at.alladin.rmbt.android.loopmode.LoopModeResults.TrafficStats;
import at.alladin.rmbt.android.loopmode.info.LoopModeTriggerItem;
import at.alladin.rmbt.android.main.AppConstants;
import at.alladin.rmbt.android.main.RMBTMainActivity;
import at.alladin.rmbt.android.test.RMBTService.RMBTBinder;
import at.alladin.rmbt.android.util.CheckTestResultDetailTask;
import at.alladin.rmbt.android.util.CheckTestResultTask;
import at.alladin.rmbt.android.util.ConfigHelper;
import at.alladin.rmbt.android.util.EndTaskListener;
import at.alladin.rmbt.android.util.GeoLocation;
import at.alladin.rmbt.android.util.NotificationIDs;
import at.alladin.rmbt.android.views.ResultDetailsView.ResultDetailType;
import at.alladin.rmbt.client.helper.IntermediateResult;
import at.alladin.rmbt.client.helper.TestStatus;
import at.alladin.rmbt.client.v2.task.service.TestMeasurement;
import at.alladin.rmbt.util.model.shared.exception.ErrorStatus;

public class RMBTLoopService extends Service implements ServiceConnection {
    private static final String TAG = "RMBTLoopService";

    private static final boolean SHOW_DEV_BUTTONS = false;

    private WakeLock partialWakeLock;
    private WakeLock dimWakeLock;

    final LoopModeResults loopModeResults = new LoopModeResults();

    public static interface RMBTLoopServiceConnection {
        RMBTLoopService getRMBTLoopService();
    }

    public class RMBTLoopBinder extends Binder {
        public RMBTLoopService getService() {
            return RMBTLoopService.this;
        }
    }

    private class LocalGeoLocation extends GeoLocation {
        public LocalGeoLocation(Context ctx) {
            super(ctx, ConfigHelper.isLoopModeGPS(ctx), 1000, 5);
        }

        @Override
        public void onLocationChanged(Location curLocation) {
            if (lastTestLocation != null) {
                final float distance = curLocation.distanceTo(lastTestLocation);
                loopModeResults.setLastDistance(distance);
                loopModeResults.setLastAccuracy(curLocation.getAccuracy());
                Log.d(TAG, "location distance: " + distance + "; maxMovement: " + loopModeResults.getMaxMovement());
                onAlarmOrLocation(false);
            }
            lastLocation = curLocation;
        }
    }

    private static final String ACTION_ALARM = "at.alladin.rmbt.android.Alarm";
    private static final String ACTION_WAKEUP_ALARM = "at.alladin.rmbt.android.WakeupAlarm";
    public static final String ACTION_STOP = "at.alladin.rmbt.android.Stop";
    public static final String ACTION_START = "at.alladin.rmbt.android.Start";
    private static final String ACTION_FORCE = "at.alladin.rmbt.android.Force";

    public static final String BROADCAST_QOS_RESULT_FETCHED = "at.alladin.rmbt.android.test.RMBTLoopService.qosResultFetched";
    public static final String BROADCAST_TEST_RESULT_FETCHED = "at.alladin.rmbt.android.test.RMBTLoopService.testResultFetched";

    private static final long ACCEPT_INACCURACY = 1000; // accept 1 sec inaccuracy

    private final RMBTLoopBinder localBinder = new RMBTLoopBinder();

    private AlarmManager alarmManager;
    private PendingIntent alarm;
    private PendingIntent wakeupAlarm;
    private NotificationManager notificationManager;
    private NotificationCompat.Builder notificationBuilder;

    private LocalGeoLocation geoLocation;

    private AtomicBoolean isRunning = new AtomicBoolean();

    private Location lastLocation;
    private Location lastTestLocation;

    private RMBTService rmbtService;

    private AtomicBoolean isActive = new AtomicBoolean(false);

    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "Received intent: " + intent.getAction());
            if (RMBTService.BROADCAST_TEST_FINISHED.equals(intent.getAction())) {
                System.out.println("BROADCAST TEST FINISHED ERROR: " + rmbtService.getError());
                updateLoopModeResults(false);
                if (loopModeResults.getMaxTests() == loopModeResults.getNumberOfTests()) {
                    setFinishedNotification();
                    isActive.set(false);
                }

                isRunning.set(false);
                onAlarmOrLocation(false);
            } else if (RMBTService.BROADCAST_TEST_ABORTED.equals(intent.getAction())) {
                isRunning.set(false);
                onStartCommand(new Intent(ACTION_STOP), 0, 0);
            }
        }
    };

    private BroadcastReceiver rmbtTaskReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "RMBTTask Intent: " + intent.getAction());
        }

    };

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        final RMBTBinder binder = (RMBTBinder) service;
        rmbtService = binder.getService();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        rmbtService = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return localBinder;
    }

    public void triggerTest() {
        loopModeResults.setStatus(Status.RUNNING);
        isRunning.set(true);
        loopModeResults.setNumberOfTests(loopModeResults.getNumberOfTests() + 1);
        lastTestLocation = lastLocation;
        loopModeResults.setLastDistance(0);
        loopModeResults.setLastTestTime(SystemClock.elapsedRealtime());
        final Intent service = new Intent(RMBTService.ACTION_LOOP_TEST, null, this, RMBTService.class);
        ConfigHelper.setLoopModeTestCounter(getApplicationContext(), loopModeResults.getNumberOfTests());
        startService(service);
        updateNotification();
    }

    private void readConfig() {
        loopModeResults.setMinDelay((long) (ConfigHelper.getLoopModeMinDelay(this) * 1000));
        loopModeResults.setMaxDelay(
                (long) (ConfigHelper.getLoopModeMaxDelay(this) * 1000 * AppConstants.LOOP_MODE_TIME_MOD));
        loopModeResults.setMaxMovement(ConfigHelper.getLoopModeMaxMovement(this));
        loopModeResults.setMaxTests(ConfigHelper.getLoopModeMaxTests(this));
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "created");
        super.onCreate();

        partialWakeLock = ((PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE))
                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "RMBTLoopWakeLock");
        partialWakeLock.acquire();

        alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        readConfig();

        geoLocation = new LocalGeoLocation(this);
        geoLocation.start();

        notificationBuilder = createNotificationBuilder();

        startForeground(NotificationIDs.LOOP_ACTIVE, notificationBuilder.build());
        final IntentFilter actionFilter = new IntentFilter(RMBTService.BROADCAST_TEST_FINISHED);
        actionFilter.addAction(RMBTService.BROADCAST_TEST_ABORTED);
        registerReceiver(receiver, actionFilter);

        final IntentFilter rmbtTaskActionFilter = new IntentFilter(RMBTTask.BROADCAST_TEST_START);
        registerReceiver(rmbtTaskReceiver, rmbtTaskActionFilter);

        final Intent alarmIntent = new Intent(ACTION_ALARM, null, this, getClass());
        alarm = PendingIntent.getService(this, 0, alarmIntent, 0);

        if (ConfigHelper.isLoopModeWakeLock(this)) {
            Log.d(TAG, "using dimWakeLock");
            dimWakeLock = ((PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE))
                    .newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
                            | PowerManager.ON_AFTER_RELEASE, "RMBTLoopDimWakeLock");
            dimWakeLock.acquire();

            final Intent wakeupAlarmIntent = new Intent(ACTION_WAKEUP_ALARM, null, this, getClass());
            wakeupAlarm = PendingIntent.getService(this, 0, wakeupAlarmIntent, 0);

            final long now = SystemClock.elapsedRealtime();
            alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + 10000, 10000, wakeupAlarm);
        }

        bindService(new Intent(getApplicationContext(), RMBTService.class), this, BIND_AUTO_CREATE);
    }

    private void setAlarm(long millis) {
        Log.d(TAG, "setAlarm: " + millis);

        final long now = SystemClock.elapsedRealtime();
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + millis, alarm);
    }

    private void stopAlarm() {
        if (alarmManager != null) {
            alarmManager.cancel(alarm);
            alarmManager.cancel(wakeupAlarm);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: " + intent);

        readConfig();
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_START.equals(action)) {
                isActive.set(true);
            } else if (action != null && action.equals(ACTION_STOP)) {
                stopAlarm();
                loopModeResults.setStatus(Status.IDLE);
                isActive.set(false);
                stopForeground(true);
                stopSelf();
            }

            if (isActive.get()) {
                if (action != null && action.equals(ACTION_FORCE))
                    onAlarmOrLocation(true);
                else if (action != null && action.equals(ACTION_ALARM))
                    onAlarmOrLocation(false);
                else if (action != null && action.equals(ACTION_WAKEUP_ALARM))
                    onWakeup();
                else {
                    if (loopModeResults.getLastTestTime() == 0) {
                        Toast.makeText(this, R.string.loop_started, Toast.LENGTH_LONG).show();
                        onAlarmOrLocation(true);
                    } else {
                        Toast.makeText(this, R.string.loop_already_active, Toast.LENGTH_LONG).show();
                        onAlarmOrLocation(true);
                    }
                }
            } else {

            }
        }
        return START_NOT_STICKY;
    }

    @SuppressLint("Wakelock")
    private void onWakeup() {
        if (dimWakeLock != null) {
            if (dimWakeLock.isHeld())
                dimWakeLock.release();
            dimWakeLock.acquire();
        }
    }

    private void onAlarmOrLocation(boolean force) {
        if (!isActive.get())
            return;

        updateNotification();
        final long now = SystemClock.elapsedRealtime();
        final long lastTestDelta = now - loopModeResults.getLastTestTime();

        Log.d(TAG, "onAlarmOrLocation; force:" + force);

        if (isRunning.get()) {
            if (lastTestDelta >= RMBTService.DEADMAN_TIME) {
                Log.e(TAG, "still running after " + lastTestDelta + " - assuming crash");
                isRunning.set(false); // assume crash and carry on
            } else {
                setAlarm(10000); // check again in 10s
                return;
            }
        }

        boolean run = false;

        if (force)
            run = true;

        if (!run) {
            Log.d(TAG, "lastTestDelta: " + lastTestDelta);

            long delay = loopModeResults.getMaxDelay();
            if (loopModeResults.getLastDistance() >= loopModeResults.getMaxMovement()
                    && loopModeResults.getLastAccuracy() > 0.0f
                    && loopModeResults.getLastAccuracy() <= AppConstants.LOOP_MODE_GPS_ACCURACY_CRITERIA) {
                Log.d(TAG, "lastDistance >= maxMovement; triggerTest");
                delay = loopModeResults.getMinDelay();
            }

            if (lastTestDelta + ACCEPT_INACCURACY >= delay) {
                Log.d(TAG, "accept delay (" + delay + "); triggerTest");
                run = true;
            }
        }

        if (run)
            triggerTest();

        if (loopModeResults.getMaxTests() == 0
                || loopModeResults.getNumberOfTests() <= loopModeResults.getMaxTests()) {
            setAlarm(10000);
        } else
            stopSelf();

        //setAlarm(delay - lastTestDelta);
    }

    @Override
    public void onDestroy() {
        if (partialWakeLock != null && partialWakeLock.isHeld())
            partialWakeLock.release();
        if (dimWakeLock != null && dimWakeLock.isHeld())
            dimWakeLock.release();
        unbindService(this);
        Log.d(TAG, "destroyed");
        super.onDestroy();
        unregisterReceiver(receiver);
        unregisterReceiver(rmbtTaskReceiver);
        if (geoLocation != null) {
            geoLocation.stop();
        }
        stopAlarm();
    }

    private NotificationCompat.Builder createNotificationBuilder() {
        final Resources res = getResources();

        Intent notificationIntent = new Intent(getApplicationContext(), RMBTMainActivity.class);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent openAppIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);

        final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.stat_icon_loop)
                .setContentTitle(res.getText(R.string.loop_notification_title))
                .setTicker(res.getText(R.string.loop_notification_ticker)).setContentIntent(openAppIntent);

        setNotificationText(builder);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            if (SHOW_DEV_BUTTONS) {
                final Intent stopIntent = new Intent(ACTION_STOP, null, getApplicationContext(), getClass());
                final PendingIntent stopPIntent = PendingIntent.getService(getApplicationContext(), 0, stopIntent,
                        0);
                final Intent forceIntent = new Intent(ACTION_FORCE, null, getApplicationContext(), getClass());
                final PendingIntent forcePIntent = PendingIntent.getService(getApplicationContext(), 0, forceIntent,
                        0);

                addActionToNotificationBuilder(builder, stopPIntent, forcePIntent);
            }
        }

        return builder;
    }

    public Intent getStopAction() {
        return new Intent(ACTION_STOP, null, getApplicationContext(), getClass());
    }

    private NotificationCompat.BigTextStyle bigTextStyle;

    private void setNotificationText(NotificationCompat.Builder builder) {
        final Resources res = getResources();

        final long now = SystemClock.elapsedRealtime();
        final long lastTestDelta = loopModeResults.getLastTestTime() == 0 ? 0
                : now - loopModeResults.getLastTestTime();

        final String elapsedTimeString = LoopModeTriggerItem.formatSeconds(Math.round(lastTestDelta / 1000), 1);

        final CharSequence textTemplate = res.getText(R.string.loop_notification_text_without_stop);
        final CharSequence text = MessageFormat.format(textTemplate.toString(), loopModeResults.getNumberOfTests(),
                elapsedTimeString, Math.round(loopModeResults.getLastDistance()));
        builder.setContentText(text);

        if (bigTextStyle == null) {
            bigTextStyle = (new NotificationCompat.BigTextStyle());
            builder.setStyle(bigTextStyle);
        }

        bigTextStyle.bigText(text);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private static void addActionToNotificationBuilder(NotificationCompat.Builder builder, PendingIntent stopIntent,
            PendingIntent forceIntent) {
        builder.addAction(android.R.drawable.ic_menu_delete, "stop", stopIntent);
        builder.addAction(android.R.drawable.ic_media_play, "force", forceIntent);
    }

    private void updateNotification() {
        setNotificationText(notificationBuilder);
        final Notification notification = notificationBuilder.build();
        notificationManager.notify(NotificationIDs.LOOP_ACTIVE, notification);
    }

    private void setFinishedNotification() {
        Intent notificationIntent = new Intent(getApplicationContext(), RMBTMainActivity.class);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent openAppIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);

        final Resources res = getResources();
        final Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.stat_icon_loop)
                .setContentTitle(res.getText(R.string.loop_notification_finished_title))
                .setTicker(res.getText(R.string.loop_notification_finished_ticker)).setContentIntent(openAppIntent)
                .build();

        //notification.flags |= Notification.FLAG_AUTO_CANCEL;

        notificationManager.notify(NotificationIDs.LOOP_ACTIVE, notification);
    }

    public boolean isRunning() {
        return isRunning.get();
    }

    public Location getLastTestLocation() {
        return lastTestLocation;
    }

    public boolean isActive() {
        return isActive.get();
    }

    public LoopModeResults getLoopModeResults() {
        return loopModeResults;
    }

    public LoopModeResults updateLoopModeResults(boolean isDuringTest) {
        if (rmbtService != null && isActive()) {
            final TrafficStats ts = new TrafficStats(0, 0);
            if (rmbtService.getTrafficMeasurementMap() != null
                    && rmbtService.getTrafficMeasurementMap().size() > 0) {
                for (final TestMeasurement tm : rmbtService.getTrafficMeasurementMap().values()) {
                    ts.add(new TrafficStats(tm.getTxBytes(), tm.getRxBytes()));
                }
            }

            IntermediateResult intermediateResult = loopModeResults.getCurrentTest().getResult();

            final LoopModeLastTestResults lastTestResults = new LoopModeLastTestResults();

            if (isDuringTest) {
                //during a test:
                //update current traffic stats
                loopModeResults.setStatus(Status.RUNNING);
                final LoopModeCurrentTest test = loopModeResults.getCurrentTest();
                test.setResult(rmbtService.getIntermediateResult(intermediateResult));
                test.setIp(rmbtService.getIP());
                test.setServerName(rmbtService.getServerName());
                test.setStartTimeMillis(rmbtService.getStartTimeMillis());

                loopModeResults.setCurrentTrafficStats(ts);
            } else {
                //after test:
                //update all test results
                loopModeResults.setStatus(Status.IDLE);

                if ((intermediateResult = rmbtService.getIntermediateResult(intermediateResult)) != null) {
                    //if test could be started (= intermediate results are available):
                    final TestStatus testStatus = intermediateResult.status;

                    switch (testStatus) {
                    case ABORTED:
                    case ERROR:
                        lastTestResults.setStatus(RMBTLoopLastTestStatus.ERROR);
                    default:
                        lastTestResults.setStatus(LoopModeLastTestResults.RMBTLoopLastTestStatus.OK);
                    }

                    if (lastTestResults.getStatus().equals(LoopModeLastTestResults.RMBTLoopLastTestStatus.OK)) {
                        final String testUuid = rmbtService == null ? null : rmbtService.getTestUuid(true);
                        if (testUuid == null) {
                            //last test uuid not available = test results cannot be fetched
                            lastTestResults.setStatus(
                                    LoopModeLastTestResults.RMBTLoopLastTestStatus.TEST_RESULTS_MISSING_UUID);
                        } else {
                            lastTestResults.setTestUuid(testUuid);
                            try {
                                //test uuid available: fetch test results
                                final CheckTestResultTask testResultTask = new CheckTestResultTask(
                                        getApplicationContext());
                                testResultTask.setEndTaskListener(new EndTaskListener() {

                                    @Override
                                    public void taskEnded(JSONArray result) {
                                        //got test results. read open-test-uuid and fetch test details and qos results
                                        boolean hasError = testResultTask.hasError();

                                        //if result is null or has no length something went wrong
                                        if (result == null || result.length() == 0) {
                                            hasError = true;
                                        }

                                        //try to get the open test uuid
                                        String openTestUuid = null;
                                        if (!hasError) {
                                            try {
                                                openTestUuid = result.getJSONObject(0).optString("open_test_uuid");
                                                lastTestResults.setOpenTestUuid(openTestUuid);
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                                hasError = true;
                                            }
                                        }

                                        //if error occurred or the open test uuid is not set then set the last test result to error 
                                        if (hasError || openTestUuid == null) {
                                            lastTestResults
                                                    .setTestResultStatus(RMBTLoopFetchingResultsStatus.ERROR);
                                            lastTestResults.setQosResultStatus(RMBTLoopFetchingResultsStatus.ERROR);
                                            lastTestResults.setStatus(RMBTLoopLastTestStatus.ERROR);
                                            return;
                                        }

                                        //everything went right until now. fetching opendata and qos results is allowed
                                        final CheckTestResultDetailTask testResultDetailTask = new CheckTestResultDetailTask(
                                                getApplicationContext(), ResultDetailType.OPENDATA);
                                        testResultDetailTask.setEndTaskListener(new EndTaskListener() {

                                            @Override
                                            public void taskEnded(JSONArray result) {
                                                lastTestResults.setTestResults(result);
                                                if (lastTestResults
                                                        .getTestResultStatus() == RMBTLoopFetchingResultsStatus.OK) {
                                                    try {
                                                        final long pingNs = (long) (lastTestResults.getTestResults()
                                                                .getDouble("ping_ms") * 1e6);
                                                        final long uploadKbit = lastTestResults.getTestResults()
                                                                .getLong("upload_kbit");
                                                        final long downloadKbit = lastTestResults.getTestResults()
                                                                .getLong("download_kbit");
                                                        loopModeResults.updateMedians(pingNs, uploadKbit,
                                                                downloadKbit);
                                                        RMBTLoopService.this.sendBroadcast(
                                                                new Intent(BROADCAST_TEST_RESULT_FETCHED));
                                                    } catch (JSONException e) {
                                                        e.printStackTrace();
                                                        lastTestResults.setTestResultStatus(
                                                                RMBTLoopFetchingResultsStatus.ERROR);
                                                    }
                                                }
                                            }
                                        });
                                        testResultDetailTask.execute(lastTestResults.getOpenTestUuid());

                                        final CheckTestResultDetailTask qosResultTask = new CheckTestResultDetailTask(
                                                getApplicationContext(), ResultDetailType.QUALITY_OF_SERVICE_TEST);

                                        qosResultTask.setEndTaskListener(new EndTaskListener() {

                                            @Override
                                            public void taskEnded(JSONArray result) {
                                                lastTestResults.setQoSResult(result);
                                                RMBTLoopService.this
                                                        .sendBroadcast(new Intent(BROADCAST_QOS_RESULT_FETCHED));
                                                if (lastTestResults
                                                        .getQosResultStatus() == RMBTLoopFetchingResultsStatus.OK) {
                                                    System.out.println("GOT QOS RESULTS...");
                                                    System.out.println(lastTestResults.getQosResult()
                                                            .getQoSStatistics().toString());
                                                }
                                            }
                                        });

                                        qosResultTask.execute(lastTestResults.getTestUuid());
                                    }
                                });

                                //after setting everything up we can start the result task:
                                testResultTask.execute(lastTestResults.getTestUuid());
                            } catch (Exception e) {
                                e.printStackTrace();
                                lastTestResults.setStatus(RMBTLoopLastTestStatus.ERROR);
                            }
                        }
                    }

                    loopModeResults.updateTrafficStats(ts);
                } else {
                    //no intermediate results found, some pre-speedtest error occurred (connection error? test rejected?)
                    final Set<ErrorStatus> errorSet = rmbtService.getErrorStatusList();
                    RMBTLoopLastTestStatus lastStatus = RMBTLoopLastTestStatus.ERROR;
                    if (errorSet != null) {
                        if (errorSet.contains(ErrorStatus.TEST_REJECTED)) {
                            lastStatus = RMBTLoopLastTestStatus.REJECTED;
                        }
                    }

                    lastTestResults.setStatus(lastStatus);
                }

                //finally add a "last test result" object to the loop mode result
                loopModeResults.setLastTestResults(lastTestResults);
            }
        }

        return loopModeResults;
    }
}