com.samknows.measurement.test.TestExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.samknows.measurement.test.TestExecutor.java

Source

/*
2013 Measuring Broadband America Program
Mobile Measurement Android Application
Copyright (C) 2012  SamKnows Ltd.
    
The FCC Measuring Broadband America (MBA) Program's Mobile Measurement Effort developed in cooperation with SamKnows Ltd. and diverse stakeholders employs an client-server based anonymized data collection approach to gather broadband performance data in an open and transparent manner with the highest commitment to protecting participants privacy.  All data collected is thoroughly analyzed and processed prior to public release to ensure that subscribers privacy interests are protected.
    
Data related to the radio characteristics of the handset, information about the handset type and operating system (OS) version, the GPS coordinates available from the handset at the time each test is run, the date and time of the observation, and the results of active test results are recorded on the handset in JSON(JavaScript Object Notation) nested data elements within flat files.  These JSON files are then transmitted to storage servers at periodic intervals after the completion of active test measurements.
    
This Android application source code is made available under the GNU GPL2 for testing purposes only and intended for participants in the SamKnows/FCC Measuring Broadband American program.  It is not intended for general release and this repository may be disabled at any time.
    
    
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.samknows.measurement.test;

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;

import com.samknows.measurement.AppSettings;
import com.samknows.measurement.Constants;
import com.samknows.measurement.Logger;
import com.samknows.measurement.R;
import com.samknows.measurement.activity.components.UIUpdate;
import com.samknows.measurement.schedule.TestDescription;
import com.samknows.measurement.schedule.TestGroup;
import com.samknows.measurement.schedule.condition.ConditionGroup;
import com.samknows.measurement.schedule.condition.ConditionGroupResult;
import com.samknows.measurement.schedule.datacollection.BaseDataCollector;
import com.samknows.measurement.storage.DBHelper;

import com.samknows.measurement.storage.ResultsContainer;
import com.samknows.measurement.storage.TestBatch;
import com.samknows.measurement.util.DCSStringBuilder;
import com.samknows.tests.ClosestTarget;
import com.samknows.tests.Param;
import com.samknows.tests.Test;
import com.samknows.tests.TestFactory;

public class TestExecutor {
    private static final String JSON_SUBMISSION_TYPE = "submission_type";
    private static final String TAG = TestExecutor.class.getName();
    private TestContext tc;
    private Test executingTest;
    private long lastTestBytes;
    private Thread startThread = null;
    public ResultsContainer rc;

    public TestExecutor(TestContext tc) {
        super();
        this.tc = tc;
        rc = new ResultsContainer();
    }

    public TestResult execute(ConditionGroup cg, TestDescription td) {
        showNotification(tc.getString(R.string.ntf_checking_conditions));
        TestResult result = new TestResult();
        if (cg != null) {
            try {
                ConditionGroupResult c = (ConditionGroupResult) cg.testBefore(tc).get();
                result.add(c);
            } catch (Exception e) {
                Logger.e(this, "Error in running a test condition: " + e.getMessage());
            }
        }

        if (result.isSuccess || result.isFailQuiet()) {
            executeTest(td, result);

        }
        Logger.d(TAG, "result test: " + (result.isSuccess ? "OK" : "FAIL"));

        if (result.isSuccess && cg != null) {
            ConditionGroupResult cgr = cg.testAfter(tc);
            result.add(cgr);
            rc.addCondition(result.json_results);
        }

        if (cg != null) {
            cg.release(tc);
        }

        Logger.d(this, rc.getJSON().toString());

        // TestResultsManager.saveResult(tc.getServiceContext(),
        // result.results);
        cancelNotification();
        return result;
    }

    public void executeTest(TestDescription td, TestResult result) {
        try {
            List<Param> params = tc.paramsManager.prepareParams(td.params);

            executingTest = TestFactory.create(td.type, params);
            if (executingTest != null) {
                getPartialResult();
                Logger.d(TestExecutor.class, "start to execute test: " + td.displayName);
                showNotification(tc.getString(R.string.ntf_running_test) + td.displayName);

                //execute the test in a new thread and kill it it it doesn't terminate after
                //Constants.WAIT_TEST_BEFORE_ABORT
                Thread t = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        executingTest.execute();
                    }
                });
                t.start();
                t.join(Constants.WAIT_TEST_BEFORE_ABORT);
                if (t.isAlive()) {
                    Logger.e(this, "Test is still runnuing after " + Constants.WAIT_TEST_BEFORE_ABORT / 1000
                            + " seconds.");
                    t.interrupt();
                    t = null;
                } else {
                    lastTestBytes = executingTest.getNetUsage();
                    result.isSuccess = executingTest.isSuccessful();
                    String out = executingTest.getOutputString();
                    result.addTestString(out);
                    rc.addTest(executingTest.getJSONResult());
                    // HACK TO INCLUDE THE JUDPJITTER RESULTS
                    if (td.type.equalsIgnoreCase("latency")) {
                        String[] judp = executingTest.getOutputFields();
                        DCSStringBuilder jjitter = new DCSStringBuilder();
                        String jitter = "" + (Integer.parseInt(judp[5]) - Integer.parseInt(judp[6]));
                        String sent = "" + (Integer.parseInt(judp[9]) + Integer.parseInt(judp[10]));
                        String received = "" + (Integer.parseInt(judp[9]) - Integer.parseInt(judp[10]));
                        jjitter.append("JUDPJITTER");
                        jjitter.append(judp[1]); // TIMESTAMP
                        jjitter.append(judp[2]); // STATUS
                        jjitter.append(judp[3]); // TARGET
                        jjitter.append(judp[4]); // TARGET IP ADDRESS
                        jjitter.append(128); // PACKETSIZE
                        jjitter.append(0); // BITRATE
                        jjitter.append(0); // DURATION
                        jjitter.append(sent); // PACKETS SENT UP
                        jjitter.append(sent); // PACKETS SENT DOWN
                        jjitter.append(received); // PACKETS RECEIVED UP
                        jjitter.append(received); // PACKETS RECEIVED DOWN
                        jjitter.append(jitter); // JITTER UP
                        jjitter.append(jitter); // JITTER DOWN
                        jjitter.append(judp[5]); // AVERAGE RTT
                        result.addTestString(jjitter.build());
                    }

                    if (result.isSuccess) {
                        tc.paramsManager.processOutParams(out, td.outParamsDescription);
                        if (executingTest.getHumanReadable() != null) {
                            HashMap<String, String> last_values = executingTest.getHumanReadable().getValues();
                            for (String key : last_values.keySet()) {
                                String value = last_values.get(key);
                                Logger.d(TestExecutor.class, "last_" + key + " " + value);
                                AppSettings.getInstance().saveString("last_" + key, value);
                            }
                        }
                    }

                    Logger.d(TAG, "finished execution test: " + td.type);
                }
            } else {
                Logger.e(TAG, "Can't find test for: " + td.type, new RuntimeException());
                result.isSuccess = false;
            }
        } catch (Throwable e) {
            Logger.e(this, "Error in executing the test. ", e);
            result.isSuccess = false;
        } finally {
            cancelNotification();
        }
    }

    public int getProgress() {
        if (executingTest != null) {
            return executingTest.getProgress();
        }
        return -1;
    }

    public void getPartialResult() {
        if (executingTest instanceof ClosestTarget) {
            final ClosestTarget ct = (ClosestTarget) executingTest;
            Runnable r = new Runnable() {
                public void run() {
                    Logger.d(TestExecutor.class, "getPartialResult started");
                    while (true) {
                        ClosestTarget.Result r = ct.getPartialResults();
                        if (r == null) {
                            break;
                        }
                        String target = tc.config.findHostName(r.currbest_target);
                        r.currbest_target = target;
                        JSONObject p = UIUpdate.getClosestTargetPartialResult(r);
                        tc.publish(p);
                        Logger.d(TestExecutor.class, p.toString());
                    }
                }
            };
            new Thread(r).start();
        }

    }

    public Test.HumanReadable getHumanReadable() {
        if (executingTest == null) {
            return null;
        }
        return executingTest.getHumanReadable();
    }

    public String getHumanReadableResult() {
        if (executingTest != null) {
            return executingTest.getHumanReadableResult();
        } else {
            return "failed to find test!";
        }
    }

    @SuppressWarnings("deprecation")
    public void showNotification(String message) {
        String title = tc.getString(R.string.ntf_title);

        NotificationManager manager = (NotificationManager) tc.getSystemService(Context.NOTIFICATION_SERVICE);
        Notification n = new Notification(R.drawable.icon, message, System.currentTimeMillis());
        PendingIntent intent = PendingIntent.getService(tc.getServiceContext(), Constants.RC_NOTIFICATION,
                new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
        n.setLatestEventInfo(tc.getServiceContext(), title, message, intent);
        manager.notify(Constants.NOTIFICATION_ID, n);
    }

    public void cancelNotification() {
        NotificationManager manager = (NotificationManager) tc.getSystemService(Context.NOTIFICATION_SERVICE);
        manager.cancel(Constants.NOTIFICATION_ID);
    }

    public void startInBackGround() {
        startThread = new Thread(new Runnable() {
            public void run() {
                start();
            }
        });
        startThread.start();
    }

    public void start() {
        for (BaseDataCollector collector : tc.config.dataCollectors) {
            if (collector.isEnabled)
                collector.start(tc);
        }
    }

    public void stop() {
        if (startThread != null) {
            try {
                startThread.join(1000);
            } catch (InterruptedException ie) {
                Logger.e(this, "Exception while waiting for the start thread to finish");
            }
        }
        for (BaseDataCollector collector : tc.config.dataCollectors) {
            if (collector.isEnabled) {
                collector.stop(tc);
                // TestResultsManager.saveResult(tc.getServiceContext(),
                // collector.getOutput());
                rc.addMetric(collector.getJSONOutput());
            }
        }
    }

    public TestResult executeGroup(TestGroup tg) {
        long startTime = System.currentTimeMillis();
        List<TestDescription> tds = new ArrayList<TestDescription>();
        for (int test_id : tg.testIds) {
            tds.add(tc.config.findTestById(test_id));
        }
        ConditionGroup cg = tc.config.getConditionGroup(tg.conditionGroupId);
        showNotification(tc.getString(R.string.ntf_checking_conditions));
        TestResult result = new TestResult();
        if (cg != null) {
            try {
                ConditionGroupResult c = (ConditionGroupResult) cg.testBefore(tc).get();
                result.add(c);
            } catch (Exception e) {
                Logger.e(this, "Error in running a test condition: " + e.getMessage());
            }
        }

        for (TestDescription td : tds) {
            executeTest(td, result);
        }
        List<JSONObject> testsResults = new ArrayList<JSONObject>();
        for (String out : result.results) {
            testsResults.addAll(com.samknows.measurement.storage.TestResult.testOutput(out));
        }

        if (cg != null) {
            ConditionGroupResult cgr = cg.testAfter(tc);
            result.add(cgr);
            rc.addCondition(result.json_results);
            cg.release(tc);
        }
        List<JSONObject> passiveMetrics = new ArrayList<JSONObject>();
        for (BaseDataCollector c : tc.config.dataCollectors) {
            if (c.isEnabled) {
                for (JSONObject o : c.getPassiveMetric()) {
                    passiveMetrics.add(o);
                }
            }
        }
        JSONObject batch = new JSONObject();
        try {
            batch.put(TestBatch.JSON_DTIME, startTime);
            batch.put(TestBatch.JSON_RUNMANUALLY, "0");
        } catch (JSONException je) {
            Logger.e(this, "Error in creating test batch object: " + je.getMessage());
        }
        DBHelper db = new DBHelper(tc.getServiceContext());

        db.insertTestBatch(batch, testsResults, passiveMetrics);

        cancelNotification();
        return result;

    }

    public TestResult executeGroup(long groupId) {
        TestGroup tg = tc.config.findTestGroup(groupId);
        if (tg == null) {
            Logger.e(this, "can not find test for id: " + groupId);
        } else {
            return executeGroup(tg);
        }

        return new TestResult();
    }

    public TestResult execute(long testId) {
        TestDescription td = tc.config.findTest(testId);
        if (td != null) {
            ConditionGroup cg = tc.config.getConditionGroup(td.conditionGroupId);
            return execute(cg, td);
        } else {
            Logger.e(this, "can not find test for id: " + testId);
        }
        return new TestResult();
    }

    public void save(String type) {
        rc.addExtra(JSON_SUBMISSION_TYPE, type);
        TestResultsManager.saveResult(tc.getServiceContext(), rc);
    }

    public long getLastTestByte() {
        return lastTestBytes;
    }

}