org.mozilla.mozstumbler.service.stumblerthread.scanners.GPSScanner.java Source code

Java tutorial

Introduction

Here is the source code for org.mozilla.mozstumbler.service.stumblerthread.scanners.GPSScanner.java

Source

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.mozstumbler.service.stumblerthread.scanners;

import android.content.Context;
import android.content.Intent;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import org.mozilla.mozstumbler.service.AppGlobals;
import org.mozilla.mozstumbler.service.AppGlobals.ActiveOrPassiveStumbling;
import org.mozilla.mozstumbler.service.utils.TelemetryWrapper;
import org.mozilla.mozstumbler.svclocator.services.log.LoggerUtil;

public class GPSScanner implements LocationListener {
    public static final String ACTION_BASE = AppGlobals.ACTION_NAMESPACE + ".GPSScanner.";
    public static final String ACTION_GPS_UPDATED = ACTION_BASE + "GPS_UPDATED";
    public static final String ACTION_ARG_TIME = AppGlobals.ACTION_ARG_TIME;
    public static final String SUBJECT_LOCATION_LOST = "location_lost";
    public static final String SUBJECT_NEW_LOCATION = "new_location";
    public static final String NEW_LOCATION_ARG_LOCATION = "location";
    public static final int MIN_SAT_USED_IN_FIX = 3;

    private static final String LOG_TAG = LoggerUtil.makeLogTag(GPSScanner.class);
    private static final long ACTIVE_MODE_GPS_MIN_UPDATE_TIME_MS = 2000;
    private static final float ACTIVE_MODE_GPS_MIN_UPDATE_DISTANCE_M = 30;
    private static final long PASSIVE_GPS_MIN_UPDATE_FREQ_MS = 3000;
    private static final float PASSIVE_GPS_MOVEMENT_MIN_DELTA_M = 30;
    private final StumblerFilter stumbleFilter = new StumblerFilter();
    private final Context mContext;
    private final ScanManager mScanManager;
    private long mTelemetry_lastStartedMs;
    private GpsStatus.Listener mGPSListener;
    private int mLocationCount;
    private Location mLocation = new Location("internal");
    private boolean mIsPassiveMode;

    public GPSScanner(Context context, ScanManager scanManager) {
        mContext = context;
        mScanManager = scanManager;
    }

    public void start(final ActiveOrPassiveStumbling stumblingMode) {
        mIsPassiveMode = (stumblingMode == ActiveOrPassiveStumbling.PASSIVE_STUMBLING);
        if (mIsPassiveMode) {
            startPassiveMode();
        } else {
            startActiveMode();
        }
    }

    private boolean isGpsAvailable(LocationManager locationManager) {
        if (locationManager == null || locationManager.getProvider(LocationManager.GPS_PROVIDER) == null) {
            String msg = "No GPS available, scanning not started.";
            Log.d(LOG_TAG, msg);
            AppGlobals.guiLogError(msg);
            return false;
        }
        return true;
    }

    private void startPassiveMode() {
        LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        if (!isGpsAvailable(locationManager)) {
            return;
        }
        locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);

        final int timeDiffSec = Long.valueOf((System.currentTimeMillis() - mTelemetry_lastStartedMs) / 1000)
                .intValue();
        if (mTelemetry_lastStartedMs > 0 && timeDiffSec > 0) {
            TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_TIME_BETWEEN_STARTS_SEC, timeDiffSec);
        }
        mTelemetry_lastStartedMs = System.currentTimeMillis();
    }

    private void startActiveMode() {
        LocationManager lm = getLocationManager();
        if (!isGpsAvailable(lm)) {
            return;
        }

        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, ACTIVE_MODE_GPS_MIN_UPDATE_TIME_MS,
                ACTIVE_MODE_GPS_MIN_UPDATE_DISTANCE_M, this);

        reportLocationLost();

        mGPSListener = new GpsStatus.Listener() {
            public void onGpsStatusChanged(int event) {
                if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
                    GpsStatus status = getLocationManager().getGpsStatus(null);
                    Iterable<GpsSatellite> sats = status.getSatellites();

                    int satellites = 0;
                    int fixes = 0;

                    for (GpsSatellite sat : sats) {
                        satellites++;
                        if (sat.usedInFix()) {
                            fixes++;
                        }
                    }

                    if (fixes < MIN_SAT_USED_IN_FIX) {
                        reportLocationLost();
                    }
                } else if (event == GpsStatus.GPS_EVENT_STOPPED) {
                    reportLocationLost();
                }
            }
        };

        lm.addGpsStatusListener(mGPSListener);
    }

    public void stop() {
        LocationManager lm = getLocationManager();
        lm.removeUpdates(this);
        reportLocationLost();

        if (mGPSListener != null) {
            lm.removeGpsStatusListener(mGPSListener);
            mGPSListener = null;
        }
    }

    public int getLocationCount() {
        return mLocationCount;
    }

    public double getLatitude() {
        return mLocation.getLatitude();
    }

    public double getLongitude() {
        return mLocation.getLongitude();
    }

    public Location getLocation() {
        return mLocation;
    }

    private void sendToLogActivity(String msg) {
        AppGlobals.guiLogInfo(msg, "#33ccff", false, false);
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location == null) { // TODO: is this even possible??
            reportLocationLost();
            return;
        }

        String logMsg = (mIsPassiveMode) ? "[Passive] " : "[Active] ";

        String provider = location.getProvider();
        if (!provider.toLowerCase().contains("gps")) {
            sendToLogActivity(logMsg + "Discard fused/network location. Provider [" + provider + "]");
            // only interested in GPS locations
            return;
        }

        final long timeDeltaMs = location.getTime() - mLocation.getTime();

        // Seem to get greater likelihood of non-fused location with higher update freq.
        // Check dist and time threshold here, not set on the listener.
        if (mIsPassiveMode) {
            final boolean hasMoved = location.distanceTo(mLocation) > PASSIVE_GPS_MOVEMENT_MIN_DELTA_M;

            if (timeDeltaMs < PASSIVE_GPS_MIN_UPDATE_FREQ_MS || !hasMoved) {
                return;
            }
        }

        logMsg += location.toString();
        sendToLogActivity(logMsg);

        if (stumbleFilter.blockLocation(location)) {
            Log.w(LOG_TAG, "Blocked location: " + location);
            reportLocationLost();
            return;
        }

        if (AppGlobals.isDebug) {
            Log.d(LOG_TAG, "New location: " + location);
        }

        mLocation = location;

        reportNewLocationReceived(location);
        mLocationCount++;

        if (mIsPassiveMode) {
            mScanManager.newPassiveGpsLocation();
        }

        if (timeDeltaMs > 0) {
            TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_TIME_BETWEEN_RECEIVED_LOCATIONS_SEC,
                    Long.valueOf(timeDeltaMs).intValue() / 1000);
        }
    }

    @Override
    public void onProviderDisabled(String provider) {
        if (LocationManager.GPS_PROVIDER.equals(provider)) {
            reportLocationLost();
        }
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        if ((status != LocationProvider.AVAILABLE) && (LocationManager.GPS_PROVIDER.equals(provider))) {
            reportLocationLost();
        }
    }

    private LocationManager getLocationManager() {
        return (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
    }

    private void reportNewLocationReceived(Location location) {
        Intent i = new Intent(ACTION_GPS_UPDATED);
        i.putExtra(Intent.EXTRA_SUBJECT, SUBJECT_NEW_LOCATION);
        i.putExtra(NEW_LOCATION_ARG_LOCATION, location);
        i.putExtra(ACTION_ARG_TIME, System.currentTimeMillis());
        LocalBroadcastManager.getInstance(mContext).sendBroadcastSync(i);
    }

    private void reportLocationLost() {
        Intent i = new Intent(ACTION_GPS_UPDATED);
        i.putExtra(Intent.EXTRA_SUBJECT, SUBJECT_LOCATION_LOST);
        i.putExtra(ACTION_ARG_TIME, System.currentTimeMillis());
        LocalBroadcastManager.getInstance(mContext).sendBroadcastSync(i);
    }
}