at.alladin.rmbt.android.util.InformationCollector.java Source code

Java tutorial

Introduction

Here is the source code for at.alladin.rmbt.android.util.InformationCollector.java

Source

/*******************************************************************************
 * Copyright 2013-2015 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.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

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

import android.app.Fragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import at.alladin.rmbt.android.main.RMBTMainActivity;
import at.alladin.rmbt.android.main.RMBTMainMenuFragment;
import at.alladin.rmbt.android.support.telephony.CellInfoPreV18;
import at.alladin.rmbt.android.support.telephony.CellInfoSupport;
import at.alladin.rmbt.android.support.telephony.TelephonyManagerPreV18;
import at.alladin.rmbt.android.support.telephony.TelephonyManagerSupport;
import at.alladin.rmbt.android.support.telephony.TelephonyManagerV18;
import at.alladin.rmbt.client.helper.RevisionHelper;
import at.alladin.rmbt.client.v2.task.result.QoSResultCollector;

public class InformationCollector {
    /**
     * set to true if location information should be included to server request
     */
    public final static boolean BASIC_INFORMATION_INCLUDE_LOCATION = true;

    /**
     * set to true if last signal information should be included to server request
     */
    public final static boolean BASIC_INFORMATION_INCLUDE_LAST_SIGNAL_ITEM = true;

    private static final int UNKNOWN = Integer.MIN_VALUE;

    private static final String PLATTFORM_NAME = "Android";

    private static final String DEBUG_TAG = "InformationCollector";

    private static final int ACCEPT_WIFI_RSSI_MIN = -113;

    public static final int SINGAL_TYPE_NO_SIGNAL = 0;
    public static final int SINGAL_TYPE_MOBILE = 1;
    public static final int SINGAL_TYPE_RSRP = 2;
    public static final int SINGAL_TYPE_WLAN = 3;

    /** Returned by getNetwork() if Wifi */
    public static final int NETWORK_WIFI = 99;

    /** Returned by getNetwork() if Ethernet */
    public static final int NETWORK_ETHERNET = 106;

    /** Returned by getNetwork() if Bluetooth */
    public static final int NETWORK_BLUETOOTH = 107;

    private ConnectivityManager connManager = null;

    private TelephonyManager telManager = null;

    private TelephonyManagerSupport telManagerSupport = null;

    private PhoneStateListener telListener = null;

    private WifiManager wifiManager = null;

    // Handlers and Receivers for phone and network state
    private NetworkStateBroadcastReceiver networkReceiver;

    private InfoGeoLocation locationManager = null;

    private Location lastLocation;

    private String testServerName;

    private Properties fullInfo = null;

    private Context context = null;
    private boolean collectInformation;
    private boolean registerNetworkReiceiver;
    private boolean enableGeoLocation;

    private final List<GeoLocationItem> geoLocations = new ArrayList<GeoLocationItem>();
    private final List<CellLocationItem> cellLocations = new ArrayList<CellLocationItem>();
    private final List<SignalItem> signals = new ArrayList<SignalItem>();

    private final AtomicInteger signal = new AtomicInteger(Integer.MIN_VALUE);
    private final AtomicInteger signalType = new AtomicInteger(SINGAL_TYPE_NO_SIGNAL);
    private final AtomicInteger signalRsrq = new AtomicInteger(UNKNOWN);

    private final AtomicReference<SignalItem> lastSignalItem = new AtomicReference<InformationCollector.SignalItem>();
    private final AtomicInteger lastNetworkType = new AtomicInteger(TelephonyManager.NETWORK_TYPE_UNKNOWN);
    private final AtomicBoolean illegalNetworkTypeChangeDetcted = new AtomicBoolean(false);

    public static QoSResultCollector qoSResult;

    public InformationCollector(final Context context, final boolean collectInformation,
            final boolean registerNetworkReceiver) {
        this(context, collectInformation, registerNetworkReceiver, true);
    }

    public InformationCollector(final Context context, final boolean collectInformation,
            final boolean registerNetworkReceiver, final boolean enableGeoLocation) {
        // create and load default properties

        this.context = context;
        this.collectInformation = collectInformation;
        this.registerNetworkReiceiver = registerNetworkReceiver;
        this.enableGeoLocation = enableGeoLocation;

        init();

    }

    public void init() {

        // this.unload();

        reset();

        initNetwork();

        getClientInfo();

        getTelephonyInfo();

        getWiFiInfo();

        getLocationInfo();

        registerListeners();

        registerNetworkReceiver();

    }

    public void reInit() {
        reset();

        initNetwork();

        getClientInfo();

        getTelephonyInfo();

        getWiFiInfo();

        getLocationInfo();

        registerListeners();

        registerNetworkReceiver();

    }

    public void clearLists() {
        // Reset all Lists but store Last Item for next test.
        if (geoLocations.size() > 0) {
            final GeoLocationItem lastLocation = geoLocations.get(geoLocations.size() - 1);
            geoLocations.clear();
            geoLocations.add(lastLocation);
        } else
            geoLocations.clear();

        if (cellLocations.size() > 0) {
            final CellLocationItem lastCell = cellLocations.get(cellLocations.size() - 1);
            cellLocations.clear();
            cellLocations.add(lastCell);
        } else
            cellLocations.clear();

        if (signals.size() > 0) {
            final SignalItem lastSignal = signals.get(signals.size() - 1);
            signals.clear();
            signals.add(lastSignal);
        } else
            signals.clear();
    }

    public void reset() {

        testServerName = "";
        lastLocation = null;

        lastNetworkType.set(TelephonyManager.NETWORK_TYPE_UNKNOWN);
        illegalNetworkTypeChangeDetcted.set(false);

        // create and load default properties
        fullInfo = new Properties();

        fullInfo.setProperty("UUID", "");

        fullInfo.setProperty("PLATTFORM", "");
        fullInfo.setProperty("OS_VERSION", "");
        fullInfo.setProperty("API_LEVEL", "");

        fullInfo.setProperty("DEVICE", "");
        fullInfo.setProperty("MODEL", "");
        fullInfo.setProperty("PRODUCT", "");

        fullInfo.setProperty("CLIENT_NAME", "");
        fullInfo.setProperty("CLIENT_SOFTWARE_VERSION", "");

        fullInfo.setProperty("NETWORK_TYPE", "");

        fullInfo.setProperty("TELEPHONY_PHONE_TYPE", "");
        fullInfo.setProperty("TELEPHONY_DATA_STATE", "");

        fullInfo.setProperty("TELEPHONY_NETWORK_COUNTRY", "");
        fullInfo.setProperty("TELEPHONY_NETWORK_OPERATOR", "");
        fullInfo.setProperty("TELEPHONY_NETWORK_OPERATOR_NAME", "");

        fullInfo.setProperty("TELEPHONY_NETWORK_SIM_COUNTRY", "");
        fullInfo.setProperty("TELEPHONY_NETWORK_SIM_OPERATOR", "");
        fullInfo.setProperty("TELEPHONY_NETWORK_SIM_OPERATOR_NAME", "");

        fullInfo.setProperty("TELEPHONY_NETWORK_IS_ROAMING", "");

        fullInfo.setProperty("WIFI_SSID", "");
        fullInfo.setProperty("WIFI_BSSID", "");
        fullInfo.setProperty("WIFI_NETWORK_ID", "");
        // fullInfo.setProperty("WIFI_LINKSPEED", "");
        // fullInfo.setProperty("WIFI_RSSI", "");
        fullInfo.setProperty("WIFI_SUPPLICANT_STATE", "");
        fullInfo.setProperty("WIFI_SUPPLICANT_STATE_DETAIL", "");

        /*
         * fullInfo.setProperty("GEO_TIME", ""); fullInfo.setProperty("GEO_LAT",
         * ""); fullInfo.setProperty("GEO_LONG","");
         * fullInfo.setProperty("GEO_ACCURACY", "");
         * fullInfo.setProperty("GEO_ALTITUDE", "");
         * fullInfo.setProperty("GEO_BEARING", "");
         * fullInfo.setProperty("GEO_SPEED", "");
         * fullInfo.setProperty("GEO_PROVIDER", "");
         */

        clearLists();
    }

    // removes the listener
    public void unload() {

        if (locationManager != null) {
            // remove Location Listener
            locationManager.stop();
            locationManager = null;
        }

        unregisterListeners();

        if (connManager != null)
            connManager = null;

        // stop network/wifi listener
        unregisterNetworkReceiver();

        if (wifiManager != null)
            wifiManager = null;

        fullInfo = null;
    }

    private void getClientInfo() {
        final String tmpuuid = ConfigHelper.getUUID(context);

        if (tmpuuid == null || tmpuuid.length() == 0)
            fullInfo.setProperty("UUID", "");
        else
            fullInfo.setProperty("UUID", tmpuuid);

        fullInfo.setProperty("PLATTFORM", PLATTFORM_NAME);

        fullInfo.setProperty("OS_VERSION",
                android.os.Build.VERSION.RELEASE + "(" + android.os.Build.VERSION.INCREMENTAL + ")");

        fullInfo.setProperty("API_LEVEL", String.valueOf(android.os.Build.VERSION.SDK_INT));

        fullInfo.setProperty("DEVICE", android.os.Build.DEVICE);

        fullInfo.setProperty("MODEL", android.os.Build.MODEL);

        fullInfo.setProperty("PRODUCT", android.os.Build.PRODUCT);

        fullInfo.setProperty("NETWORK_TYPE", String.valueOf(getNetwork()));

        if (connManager != null) {
            final NetworkInfo activeNetworkInfo = connManager.getActiveNetworkInfo();
            if (activeNetworkInfo != null)
                fullInfo.setProperty("TELEPHONY_NETWORK_IS_ROAMING", String.valueOf(activeNetworkInfo.isRoaming()));
        }

        PackageInfo pInfo;
        String clientVersion = "";
        try {
            pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            clientVersion = pInfo.versionName;
        } catch (final NameNotFoundException e) {
            // e1.printStackTrace();
            Log.e(DEBUG_TAG, "version of the application cannot be found", e);
        }

        fullInfo.setProperty("CLIENT_NAME", Config.RMBT_CLIENT_NAME);
        fullInfo.setProperty("CLIENT_SOFTWARE_VERSION", clientVersion);
    }

    public static PackageInfo getPackageInfo(Context ctx) {
        PackageInfo pInfo = null;
        try {
            pInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0);
        } catch (final NameNotFoundException e) {
            // e1.printStackTrace();
            Log.e(DEBUG_TAG, "version of the application cannot be found", e);
        }
        return pInfo;
    }

    public static JSONObject fillBasicInfo(JSONObject object, Context ctx) throws JSONException {
        object.put("plattform", PLATTFORM_NAME);
        object.put("os_version",
                android.os.Build.VERSION.RELEASE + "(" + android.os.Build.VERSION.INCREMENTAL + ")");
        object.put("api_level", String.valueOf(android.os.Build.VERSION.SDK_INT));
        object.put("device", android.os.Build.DEVICE);
        object.put("model", android.os.Build.MODEL);
        object.put("product", android.os.Build.PRODUCT);
        object.put("language", Locale.getDefault().getLanguage());
        object.put("timezone", TimeZone.getDefault().getID());
        object.put("softwareRevision", RevisionHelper.getVerboseRevision());
        PackageInfo pInfo = getPackageInfo(ctx);
        if (pInfo != null) {
            object.put("softwareVersionCode", pInfo.versionCode);
            object.put("softwareVersionName", pInfo.versionName);
        }
        object.put("type", at.alladin.rmbt.android.util.Config.RMBT_CLIENT_TYPE);

        if (BASIC_INFORMATION_INCLUDE_LOCATION) {
            Location loc = GeoLocation.getLastKnownLocation(ctx);
            if (loc != null) {
                JSONObject locationJson = new JSONObject();
                locationJson.put("lat", loc.getLatitude());
                locationJson.put("long", loc.getLongitude());
                locationJson.put("provider", loc.getProvider());
                if (loc.hasSpeed())
                    locationJson.put("speed", loc.getSpeed());
                if (loc.hasAltitude())
                    locationJson.put("altitude", loc.getAltitude());
                locationJson.put("age", System.currentTimeMillis() - loc.getTime()); //getElapsedRealtimeNanos() would be better, but require higher API-level
                if (loc.hasAccuracy())
                    locationJson.put("accuracy", loc.getAccuracy());
                if (loc.hasSpeed())
                    locationJson.put("speed", loc.getSpeed());
                /*
                 *  would require API level 18
                if (loc.isFromMockProvider())
                   locationJson.put("mock",loc.isFromMockProvider());
                */
                object.put("location", locationJson);
            }
        }

        InformationCollector infoCollector = null;

        if (ctx instanceof RMBTMainActivity) {
            Fragment curFragment = ((RMBTMainActivity) ctx).getCurrentFragment();
            if (curFragment != null) {
                if (curFragment instanceof RMBTMainMenuFragment) {
                    infoCollector = ((RMBTMainMenuFragment) curFragment).getInformationCollector();
                }
            }
        }

        if (BASIC_INFORMATION_INCLUDE_LAST_SIGNAL_ITEM && (infoCollector != null)) {
            SignalItem signalItem = infoCollector.getLastSignalItem();
            if (signalItem != null) {
                object.put("last_signal_item", signalItem.toJson());
            } else {
                object.put("last_signal_item", JSONObject.NULL);
            }
        }

        return object;
    }

    public JSONObject getInitialInfo() {
        try {
            final JSONObject result = new JSONObject();
            fillBasicInfo(result, context);

            result.put("ndt", ConfigHelper.isNDT(context));

            result.put("testCounter", ConfigHelper.incAndGetNextTestCounter(context));
            result.put("previousTestStatus", ConfigHelper.getPreviousTestStatus(context));
            ConfigHelper.setPreviousTestStatus(context, null);
            return result;
        } catch (final JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    private void getWiFiInfo() {
        initNetwork();
        if (wifiManager != null) {
            final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            fullInfo.setProperty("WIFI_SSID",
                    String.valueOf(Helperfunctions.removeQuotationsInCurrentSSIDForJellyBean(wifiInfo.getSSID())));
            /*
             * fullInfo.setProperty("WIFI_LINKSPEED",
             * String.valueOf(wifiInfo.getLinkSpeed()));
             */
            fullInfo.setProperty("WIFI_BSSID", String.valueOf(wifiInfo.getBSSID()));
            fullInfo.setProperty("WIFI_NETWORK_ID", String.valueOf(wifiInfo.getNetworkId()));
            /*
             * fullInfo.setProperty("WIFI_RSSI",
             * String.valueOf(wifiInfo.getRssi()));
             */
            final SupplicantState wifiState = wifiInfo.getSupplicantState();
            fullInfo.setProperty("WIFI_SUPPLICANT_STATE", String.valueOf(wifiState.name()));
            final DetailedState wifiDetail = WifiInfo.getDetailedStateOf(wifiState);
            fullInfo.setProperty("WIFI_SUPPLICANT_STATE_DETAIL", String.valueOf(wifiDetail.name()));

            if (getNetwork() == NETWORK_WIFI) {

                final int rssi = wifiInfo.getRssi();
                if (rssi != -1 && rssi >= ACCEPT_WIFI_RSSI_MIN) {
                    int linkSpeed = wifiInfo.getLinkSpeed();
                    if (linkSpeed < 0) {
                        linkSpeed = 0;
                    }

                    final SignalItem signalItem = SignalItem.getWifiSignalItem(linkSpeed, rssi);
                    if (this.collectInformation) {
                        signals.add(signalItem);
                    }
                    lastSignalItem.set(signalItem);
                    signal.set(rssi);
                    signalType.set(SINGAL_TYPE_WLAN);
                    //                    Log.i(DEBUG_TAG, "Signals1: " + signals.toString());
                }
            }
        }
    }

    private void getTelephonyInfo() {
        initNetwork();
        if (telManager != null) {
            try {
                // Get Cell Location
                CellLocation.requestLocationUpdate();
            } catch (Exception e) {
                // some devices with Android 5.1 seem to throw a NPE is some cases
                e.printStackTrace();
            }

            final CellLocation cellLocation = telManager.getCellLocation();
            if (cellLocation != null && (cellLocation instanceof GsmCellLocation)) {
                final GsmCellLocation gcl = (GsmCellLocation) cellLocation;
                if (gcl.getCid() > 0 && this.collectInformation) {
                    cellLocations.add(new CellLocationItem(new CellInfoPreV18(gcl)));
                }
            }

            fullInfo.setProperty("TELEPHONY_NETWORK_OPERATOR_NAME",
                    String.valueOf(telManager.getNetworkOperatorName()));
            String networkOperator = telManager.getNetworkOperator();
            if (networkOperator != null && networkOperator.length() >= 5)
                networkOperator = String.format("%s-%s", networkOperator.substring(0, 3),
                        networkOperator.substring(3));
            fullInfo.setProperty("TELEPHONY_NETWORK_OPERATOR", String.valueOf(networkOperator));
            fullInfo.setProperty("TELEPHONY_NETWORK_COUNTRY", String.valueOf(telManager.getNetworkCountryIso()));
            fullInfo.setProperty("TELEPHONY_NETWORK_SIM_COUNTRY", String.valueOf(telManager.getSimCountryIso()));
            String simOperator = telManager.getSimOperator();
            if (simOperator != null && simOperator.length() >= 5)
                simOperator = String.format("%s-%s", simOperator.substring(0, 3), simOperator.substring(3));
            fullInfo.setProperty("TELEPHONY_NETWORK_SIM_OPERATOR", String.valueOf(simOperator));

            try // hack for Motorola Defy (#594)
            {
                fullInfo.setProperty("TELEPHONY_NETWORK_SIM_OPERATOR_NAME",
                        String.valueOf(telManager.getSimOperatorName()));
            } catch (SecurityException e) {
                e.printStackTrace();
                fullInfo.setProperty("TELEPHONY_NETWORK_SIM_OPERATOR_NAME", "s.exception");
            }

            fullInfo.setProperty("TELEPHONY_PHONE_TYPE", String.valueOf(telManager.getPhoneType()));

            try // some devices won't allow this w/o READ_PHONE_STATE. conflicts with Android API doc
            {
                fullInfo.setProperty("TELEPHONY_DATA_STATE", String.valueOf(telManager.getDataState()));
            } catch (SecurityException e) {
                e.printStackTrace();
                fullInfo.setProperty("TELEPHONY_DATA_STATE", "s.exception");
            }
            // telManager.listen(telListener,
            // PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
        }
    }

    public Location getLocationInfo() {
        if (enableGeoLocation) {
            if (locationManager == null) {
                // init location Manager
                locationManager = new InfoGeoLocation(context);
                locationManager.start();
            }
            final Location curLocation = locationManager.getLastKnownLocation();

            if (curLocation != null && this.collectInformation) {
                geoLocations.add(new GeoLocationItem(curLocation.getTime(), curLocation.getLatitude(),
                        curLocation.getLongitude(), curLocation.getAccuracy(), curLocation.getAltitude(),
                        curLocation.getBearing(), curLocation.getSpeed(), curLocation.getProvider()));
                Log.i(DEBUG_TAG, "Location: " + curLocation.toString());
            }

            return curLocation;
        } else {
            return null;
        }

    }

    // public boolean setInfo(String key, String value) {
    // if (fullInfo.containsKey(key)) {
    // fullInfo.setProperty(key, value);
    // return true;
    // } else
    // return false;
    // }

    public String getInfo(final String key) {
        String value = "";
        if (fullInfo.containsKey(key))
            value = fullInfo.getProperty(key);
        return value;
    }

    public String getUUID() {
        return fullInfo.getProperty("UUID");
    }

    public void setUUID(final String uuid) {
        if (uuid != null && uuid.length() != 0) {
            fullInfo.setProperty("UUID", uuid);
            ConfigHelper.setUUID(context, uuid);
        }
    }

    public String getOperatorName() {
        int network = getNetwork();

        if (network == NETWORK_WIFI)
            return fullInfo.getProperty("WIFI_SSID");
        else if (network == NETWORK_ETHERNET)
            return "Ethernet";
        else if (network == NETWORK_BLUETOOTH)
            return "Bluetooth";
        else {
            String TelephonyNetworkOperator = fullInfo.getProperty("TELEPHONY_NETWORK_OPERATOR");
            String TelephonyNetworkOperatorName = fullInfo.getProperty("TELEPHONY_NETWORK_OPERATOR_NAME");
            if (TelephonyNetworkOperator.length() == 0 && TelephonyNetworkOperatorName.length() == 0)
                return "-";
            else if (TelephonyNetworkOperator.length() == 0)
                return TelephonyNetworkOperatorName;
            else if (TelephonyNetworkOperatorName.length() == 0)
                return TelephonyNetworkOperator;
            else
                return String.format("%s (%s)", TelephonyNetworkOperatorName, TelephonyNetworkOperator);
        }

    }

    public ArrayList<String> getCurLocation() {

        if (geoLocations.size() > 0) {
            final int pos = geoLocations.size() - 1;
            final GeoLocationItem curLocation = geoLocations.get(pos);

            final ArrayList<String> geoInfo = new ArrayList<String>(
                    Arrays.asList(String.valueOf(curLocation.tstamp), String.valueOf(curLocation.geo_lat),
                            String.valueOf(curLocation.geo_long), String.valueOf(curLocation.accuracy),
                            String.valueOf(curLocation.altitude), String.valueOf(curLocation.bearing),
                            String.valueOf(curLocation.speed), curLocation.provider));

            return geoInfo;
        } else
            return null;
    }

    public JSONObject getResultValues(long startTimestampNs) throws JSONException {

        final JSONObject result = new JSONObject();

        final Enumeration<?> pList = fullInfo.propertyNames();

        final int network = getNetwork();
        while (pList.hasMoreElements()) {
            final String key = (String) pList.nextElement();
            boolean add = true;
            if (network == NETWORK_WIFI) {
                if (key.startsWith("TELEPHONY_")) // no mobile data if wifi
                    add = false;
            } else if (key.startsWith("WIFI_")) // no wifi data if mobile
                add = false;
            if ((network == NETWORK_ETHERNET || network == NETWORK_BLUETOOTH)
                    && (key.startsWith("TELEPHONY_") || key.startsWith("WIFI_"))) // add neither mobile nor wifi data 
                add = false;
            if (add)
                result.put(key.toLowerCase(Locale.US), fullInfo.getProperty(key));
        }

        if (geoLocations.size() > 0) {

            final JSONArray itemList = new JSONArray();

            for (int i = 0; i < geoLocations.size(); i++) {

                final GeoLocationItem tmpItem = geoLocations.get(i);

                final JSONObject jsonItem = new JSONObject();

                jsonItem.put("tstamp", tmpItem.tstamp);
                jsonItem.put("time_ns", tmpItem.tstampNano - startTimestampNs);
                jsonItem.put("geo_lat", tmpItem.geo_lat);
                jsonItem.put("geo_long", tmpItem.geo_long);
                jsonItem.put("accuracy", tmpItem.accuracy);
                jsonItem.put("altitude", tmpItem.altitude);
                jsonItem.put("bearing", tmpItem.bearing);
                jsonItem.put("speed", tmpItem.speed);
                jsonItem.put("provider", tmpItem.provider);

                itemList.put(jsonItem);
            }

            result.put("geoLocations", itemList);

        }

        if (cellLocations.size() > 0 && isMobileNetwork(network)) {

            final JSONArray itemList = new JSONArray();

            for (int i = 0; i < cellLocations.size(); i++) {

                final CellLocationItem tmpItem = cellLocations.get(i);

                final JSONObject jsonItem = new JSONObject();

                jsonItem.put("time", tmpItem.tstamp); //add for backward compatibility
                jsonItem.put("time_ns", tmpItem.tstampNano - startTimestampNs);
                jsonItem.put("location_id", tmpItem.locationId);
                Log.i(DEBUG_TAG, "Cell ID:" + tmpItem.locationId);
                jsonItem.put("area_code", tmpItem.areaCode);
                jsonItem.put("primary_scrambling_code", tmpItem.scramblingCode);
                Log.i(DEBUG_TAG, "Scrambling Code:" + tmpItem.scramblingCode);
                itemList.put(jsonItem);
            }

            result.put("cellLocations", itemList);
        }

        //Log.i(DEBUG_TAG, "Signals: " + signals.toString());

        if (signals.size() > 0) {

            final JSONArray itemList = new JSONArray();

            for (int i = 0; i < signals.size(); i++) {
                final SignalItem tmpItem = signals.get(i);

                final JSONObject jsonItem = tmpItem.toJson();
                jsonItem.put("time_ns", tmpItem.tstampNano - startTimestampNs);
                itemList.put(jsonItem);
            }

            result.put("signals", itemList);
        }

        final String tag = ConfigHelper.getTag(context);
        if (tag != null && !tag.isEmpty())
            result.put("tag", tag);

        return result;
    }

    /**
     * Lazily initializes the network managers.
     * 
     * As a side effect, assigns connectivityManager and telephonyManager.
     */
    private synchronized void initNetwork() {
        if (connManager == null) {
            final ConnectivityManager tryConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);

            final TelephonyManager tryTelephonyManager = (TelephonyManager) context
                    .getSystemService(Context.TELEPHONY_SERVICE);

            final WifiManager tryWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

            // Assign to member vars only after all the get calls succeeded,

            connManager = tryConnectivityManager;
            telManager = tryTelephonyManager;
            wifiManager = tryWifiManager;

            if (Build.VERSION.SDK_INT >= 18) {
                telManagerSupport = new TelephonyManagerV18(telManager);
            } else {
                telManagerSupport = new TelephonyManagerPreV18(telManager);
            }

            // Some interesting info to look at in the logs
            //final NetworkInfo[] infos = connManager.getAllNetworkInfo();
            //for (final NetworkInfo networkInfo : infos)
            //    Log.i(DEBUG_TAG, "Network: " + networkInfo);
        }
        assert connManager != null;
        assert telManager != null;
        assert wifiManager != null;
    }

    /** Returns the network that the phone is on (e.g. Wifi, Edge, GPRS, etc). */
    public int getNetwork() {
        int result = TelephonyManager.NETWORK_TYPE_UNKNOWN;

        if (connManager != null) {
            final NetworkInfo activeNetworkInfo = connManager.getActiveNetworkInfo();
            if (activeNetworkInfo != null) {
                final int type = activeNetworkInfo.getType();
                switch (type) {
                case ConnectivityManager.TYPE_WIFI:
                    result = NETWORK_WIFI;
                    break;

                case ConnectivityManager.TYPE_BLUETOOTH:
                    result = NETWORK_BLUETOOTH;
                    break;

                case ConnectivityManager.TYPE_ETHERNET:
                    result = NETWORK_ETHERNET;
                    break;

                case ConnectivityManager.TYPE_MOBILE:
                case ConnectivityManager.TYPE_MOBILE_DUN:
                case ConnectivityManager.TYPE_MOBILE_HIPRI:
                case ConnectivityManager.TYPE_MOBILE_MMS:
                case ConnectivityManager.TYPE_MOBILE_SUPL:
                    result = telManager.getNetworkType();
                    break;
                }
            }
        }

        /* detect change from wifi to mobile or reverse */
        final int lastNetworkType = this.lastNetworkType.get();
        if (result != TelephonyManager.NETWORK_TYPE_UNKNOWN
                && lastNetworkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
            if ((result == ConnectivityManager.TYPE_WIFI && lastNetworkType != ConnectivityManager.TYPE_WIFI)
                    || (result != ConnectivityManager.TYPE_WIFI
                            && lastNetworkType == ConnectivityManager.TYPE_WIFI))
                illegalNetworkTypeChangeDetcted.set(true);
        }
        if (result != lastNetworkType) {
            this.lastNetworkType.set(result);
            if (telListener != null)
                telListener.onSignalStrengthsChanged(null);
        }

        return result;
    }

    public boolean getIllegalNetworkTypeChangeDetcted() {
        return illegalNetworkTypeChangeDetcted.get();
    }

    /*
     * private static final String[] NETWORK_TYPES = {
     * 
     * "UNKNOWN", // 0 - NETWORK_TYPE_UNKNOWN OR NONE "GSM", // 1 -
     * NETWORK_TYPE_GPRS "EDGE", // 2 - NETWORK_TYPE_EDGE "UMTS", // 3 -
     * NETWORK_TYPE_UMTS "CDMA", // 4 - NETWORK_TYPE_CDMA "EVDO_0", // 5 -
     * NETWORK_TYPE_EVDO_0 "EVDO_A", // 6 - NETWORK_TYPE_EVDO_A "1xRTT", // 7 -
     * NETWORK_TYPE_1xRTT "HSDPA", // 8 - NETWORK_TYPE_HSDPA "HSUPA", // 9 -
     * NETWORK_TYPE_HSUPA "HSPA", // 10 - NETWORK_TYPE_HSPA "IDEN", // 11 -
     * NETWORK_TYPE_IDEN "EVDO_B", // 12 - NETWORK_TYPE_EVDO_B "LTE", // 13 -
     * NETWORK_TYPE_LTE "EHRPD", // 14 - NETWORK_TYPE_EHRPD "HSPA+", //15 -
     * NETWORK_TYPE_HSPAP };
     */

    /** Returns mobile data network connection type. */
    /*
     * private int getTelephonyNetworkType() { //assert
     * NETWORK_TYPES[14].compareTo("EHRPD") == 0;
     * 
     * int networkType = telManager.getNetworkType(); if (networkType <
     * NETWORK_TYPES.length) {
     * 
     * } else { return 0; } }
     */

    // Listeners
    private void registerListeners() {
        initNetwork();

        if (telListener == null) {
            telListener = new TelephonyStateListener();
            telManager.listen(telListener,
                    PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_CELL_LOCATION);
        }
    }

    private void unregisterListeners() {
        Log.d(DEBUG_TAG, "unregistering listener");

        if (telManager != null) {
            telManager.listen(telListener, PhoneStateListener.LISTEN_NONE);
            telListener = null;
            telManager = null;
        }
    }

    private void registerNetworkReceiver() {
        if (networkReceiver == null && registerNetworkReiceiver) {
            networkReceiver = new NetworkStateBroadcastReceiver();
            IntentFilter intentFilter;
            intentFilter = new IntentFilter();
            // intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
            intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
            Log.d(DEBUG_TAG, "registering receiver");
            context.registerReceiver(networkReceiver, intentFilter);
        }
    }

    private void unregisterNetworkReceiver() {
        Log.d(DEBUG_TAG, "unregistering receiver");
        if (networkReceiver != null)
            context.unregisterReceiver(networkReceiver);
        networkReceiver = null;
    }

    public Integer getSignal() {
        final int _signal = signal.get();
        if (_signal == Integer.MIN_VALUE)
            return null;
        return _signal;
    }

    public Integer getSignalRsrq() {
        final int _signal = signalRsrq.get();
        if (_signal == Integer.MIN_VALUE)
            return null;
        return _signal;
    }

    public int getSignalType() {
        return signalType.get();
    }

    public SignalItem getLastSignalItem() {
        return lastSignalItem.get();
    }

    public void setTestServerName(final String serverName) {
        testServerName = serverName;
    }

    public String getTestServerName() {
        return testServerName;
    }

    /**
     * Listener + recorder for mobile or wifi updates
     */
    private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(final Context context, final Intent intent) {
            final String action = intent.getAction();

            if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
                Log.d(DEBUG_TAG, "Wifi RSSI changed");

                if (getNetwork() == NETWORK_WIFI) {
                    final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                    final int rssi = wifiInfo.getRssi();
                    if (rssi != -1 && rssi >= ACCEPT_WIFI_RSSI_MIN) {
                        final SignalItem signalItem = SignalItem.getWifiSignalItem(wifiInfo.getLinkSpeed(), rssi);
                        if (InformationCollector.this.collectInformation) {
                            signals.add(signalItem);
                        }
                        lastSignalItem.set(signalItem);
                        signal.set(rssi);
                        signalType.set(SINGAL_TYPE_WLAN);
                    }
                }

            }
        }
    }

    public class TelephonyStateListener extends PhoneStateListener {
        @Override
        public void onSignalStrengthsChanged(final SignalStrength signalStrength) {
            //Log.d(DEBUG_TAG, "SignalStrength changed");
            if (signalStrength != null)
                Log.d(DEBUG_TAG, signalStrength.toString());
            final int network = getNetwork();
            int strength = UNKNOWN;
            int lteRsrp = UNKNOWN;
            int lteRsrq = UNKNOWN;
            int lteRsssnr = UNKNOWN;
            int lteCqi = UNKNOWN;
            int errorRate = UNKNOWN;

            // discard signal strength from GT-I9100G (Galaxy S II) - passes wrong info
            if (android.os.Build.MODEL != null) {
                if (android.os.Build.MODEL.equals("GT-I9100G") || android.os.Build.MODEL.equals("HUAWEI P2-6011"))
                    return;
            }

            if (network != NETWORK_WIFI && network != NETWORK_BLUETOOTH && network != NETWORK_ETHERNET) {
                if (signalStrength != null) {
                    if (network == TelephonyManager.NETWORK_TYPE_CDMA)
                        strength = signalStrength.getCdmaDbm();
                    else if (network == TelephonyManager.NETWORK_TYPE_EVDO_0
                            || network == TelephonyManager.NETWORK_TYPE_EVDO_A
                    /* || network == TelephonyManager.NETWORK_TYPE_EVDO_B */)
                        strength = signalStrength.getEvdoDbm();
                    else if (network == 13) /* TelephonyManager.NETWORK_TYPE_LTE ; not avail in api 8 */
                    {
                        try {
                            lteRsrp = (Integer) SignalStrength.class.getMethod("getLteRsrp").invoke(signalStrength);
                            lteRsrq = (Integer) SignalStrength.class.getMethod("getLteRsrq").invoke(signalStrength);
                            lteRsssnr = (Integer) SignalStrength.class.getMethod("getLteRssnr")
                                    .invoke(signalStrength);
                            lteCqi = (Integer) SignalStrength.class.getMethod("getLteCqi").invoke(signalStrength);

                            if (lteRsrp == Integer.MAX_VALUE)
                                lteRsrp = UNKNOWN;
                            if (lteRsrq == Integer.MAX_VALUE)
                                lteRsrq = UNKNOWN;
                            if (lteRsrq > 0)
                                lteRsrq = -lteRsrq; // fix invalid rsrq values for some devices (see #996)
                            if (lteRsssnr == Integer.MAX_VALUE)
                                lteRsssnr = UNKNOWN;
                            if (lteCqi == Integer.MAX_VALUE)
                                lteCqi = UNKNOWN;
                        } catch (Throwable t) {
                            t.printStackTrace();
                        }
                    } else if (signalStrength.isGsm()) {
                        try {
                            final Method getGsmDbm = SignalStrength.class.getMethod("getGsmDbm");
                            final Integer result = (Integer) getGsmDbm.invoke(signalStrength);
                            if (result != -1)
                                strength = result;
                        } catch (Throwable t) {
                        }
                        if (strength == UNKNOWN) { // fallback if not implemented
                            int dBm;
                            int gsmSignalStrength = signalStrength.getGsmSignalStrength();
                            int asu = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
                            if (asu != -1)
                                dBm = -113 + (2 * asu);
                            else
                                dBm = UNKNOWN;
                            strength = dBm;
                        }
                        errorRate = signalStrength.getGsmBitErrorRate();
                    }
                    if (lteRsrp != UNKNOWN) {
                        signal.set(lteRsrp);
                        signalType.set(SINGAL_TYPE_RSRP);
                    } else {
                        signal.set(strength);
                        signalType.set(SINGAL_TYPE_MOBILE);
                    }

                    signalRsrq.set(lteRsrq);
                }

                final SignalItem signalItem = SignalItem.getCellSignalItem(network, strength, errorRate, lteRsrp,
                        lteRsrq, lteRsssnr, lteCqi);
                if (InformationCollector.this.collectInformation) {
                    signals.add(signalItem);
                }
                lastSignalItem.set(signalItem);
            }
        }

        @Override
        public void onCellLocationChanged(CellLocation location) {
            try {
                final List<CellInfoSupport> cellInfoList = getTelManagerSupport().getAllCellInfo();
                if (cellInfoList != null && cellInfoList.size() > 0) {
                    final CellInfoSupport cellInfo = cellInfoList.get(0);
                    if (isCollectInformation()) {
                        getCellLocations().add(new CellLocationItem(cellInfo));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public Location getLastLocation() {
        return lastLocation;
    }

    public boolean isCollectInformation() {
        return collectInformation;
    }

    public boolean isRegisterNetworkReiceiver() {
        return registerNetworkReiceiver;
    }

    public boolean isEnableGeoLocation() {
        return enableGeoLocation;
    }

    public TelephonyManager getTelManager() {
        return telManager;
    }

    public TelephonyManagerSupport getTelManagerSupport() {
        return telManagerSupport;
    }

    public List<CellLocationItem> getCellLocations() {
        return cellLocations;
    }

    public static boolean isMobileNetwork(final int network) {
        return network != NETWORK_BLUETOOTH && network != NETWORK_ETHERNET && network != NETWORK_WIFI;
    }

    private class InfoGeoLocation extends GeoLocation {

        public InfoGeoLocation(final Context context) {
            super(context, ConfigHelper.isGPS(context));
        }

        @Override
        public void onLocationChanged(final Location curLocation) {
            if (curLocation != null) {
                //System.out.println("onLocationChanged..");
                lastLocation = curLocation;

                if (InformationCollector.this.collectInformation) {
                    geoLocations.add(new GeoLocationItem(curLocation.getTime(), curLocation.getLatitude(),
                            curLocation.getLongitude(), curLocation.getAccuracy(), curLocation.getAltitude(),
                            curLocation.getBearing(), curLocation.getSpeed(), curLocation.getProvider()));
                }
            }
        }
    }

    private class GeoLocationItem {

        public final long tstamp;
        public final long tstampNano;
        public final double geo_lat;
        public final double geo_long;
        public final float accuracy;
        public final double altitude;
        public final float bearing;
        public final float speed;
        public final String provider;

        public GeoLocationItem(final long tstamp, final double geo_lat, final double geo_long, final float accuracy,
                final double altitude, final float bearing, final float speed, final String provider) {
            this.tstamp = tstamp;
            this.tstampNano = System.nanoTime();
            this.geo_lat = geo_lat;
            this.geo_long = geo_long;
            this.accuracy = accuracy;
            this.altitude = altitude;
            this.bearing = bearing;
            this.speed = speed;
            this.provider = provider;
        }

    }

    public static class CellLocationItem {

        public final long tstamp;
        public final long tstampNano;
        public final int locationId;
        public final int areaCode;
        public final int scramblingCode;

        public CellLocationItem(final CellInfoSupport cellLocation) {

            tstamp = System.currentTimeMillis();
            tstampNano = System.nanoTime();
            locationId = cellLocation.getCellId();
            areaCode = cellLocation.getAreaCode();
            scramblingCode = cellLocation.getPrimaryScramblingCode();
        }

    }

    private static class SignalItem {

        public final long tstamp;
        public final int networkId;
        public final int signalStrength;
        public final int gsmBitErrorRate;
        public final int wifiLinkSpeed;
        public final int wifiRssi;

        public final int lteRsrp;
        public final int lteRsrq;
        public final int lteRssnr;
        public final int lteCqi;
        public final long tstampNano;

        public static SignalItem getWifiSignalItem(final int wifiLinkSpeed, final int wifiRssi) {
            return new SignalItem(NETWORK_WIFI, UNKNOWN, UNKNOWN, wifiLinkSpeed, wifiRssi, UNKNOWN, UNKNOWN,
                    UNKNOWN, UNKNOWN);
        }

        public static SignalItem getCellSignalItem(final int networkId, final int signalStrength,
                final int gsmBitErrorRate, final int lteRsrp, final int lteRsrq, final int lteRssnr,
                final int lteCqi) {
            return new SignalItem(networkId, signalStrength, gsmBitErrorRate, UNKNOWN, UNKNOWN, lteRsrp, lteRsrq,
                    lteRssnr, lteCqi);
        }

        private SignalItem(final int networkId, final int signalStrength, final int gsmBitErrorRate,
                final int wifiLinkSpeed, final int wifiRssi, final int lteRsrp, final int lteRsrq,
                final int lteRssnr, final int lteCqi) {
            tstamp = System.currentTimeMillis();
            tstampNano = System.nanoTime();
            this.networkId = networkId;
            this.signalStrength = signalStrength;
            this.gsmBitErrorRate = gsmBitErrorRate;
            this.wifiLinkSpeed = wifiLinkSpeed;
            this.wifiRssi = wifiRssi;
            this.lteRsrp = lteRsrp;
            this.lteRsrq = lteRsrq;
            this.lteRssnr = lteRssnr;
            this.lteCqi = lteCqi;
        }

        public JSONObject toJson() throws JSONException {
            final JSONObject jsonItem = new JSONObject();

            jsonItem.put("time", tstamp); //add for backward compatibility
            jsonItem.put("network_type_id", networkId);
            if (signalStrength != UNKNOWN) {
                jsonItem.put("signal_strength", signalStrength);
            }
            if (gsmBitErrorRate != UNKNOWN) {
                jsonItem.put("gsm_bit_error_rate", gsmBitErrorRate);
            }
            if (wifiLinkSpeed != UNKNOWN) {
                jsonItem.put("wifi_link_speed", wifiLinkSpeed);
            }
            if (wifiRssi != UNKNOWN) {
                jsonItem.put("wifi_rssi", wifiRssi);
            }
            if (lteRsrp != UNKNOWN) {
                jsonItem.put("lte_rsrp", lteRsrp);
            }
            if (lteRsrq != UNKNOWN) {
                jsonItem.put("lte_rsrq", lteRsrq);
            }
            if (lteRssnr != UNKNOWN) {
                jsonItem.put("lte_rssnr", lteRssnr);
            }
            if (lteCqi != UNKNOWN) {
                jsonItem.put("lte_cqi", lteCqi);
            }

            return jsonItem;
        }

    }
}