FusionEngine.java revision 09016ab4dd056a16809419d612cb865a14980880
16fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly/* 26fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * Copyright (C) 2012 The Android Open Source Project 36fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * 46fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * Licensed under the Apache License, Version 2.0 (the "License"); 56fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * you may not use this file except in compliance with the License. 66fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * You may obtain a copy of the License at 76fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * 86fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * http://www.apache.org/licenses/LICENSE-2.0 96fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * 106fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * Unless required by applicable law or agreed to in writing, software 116fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * distributed under the License is distributed on an "AS IS" BASIS, 126fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * See the License for the specific language governing permissions and 146fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * limitations under the License. 156fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly */ 166fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 176fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellypackage com.android.location.fused; 186fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 196fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport java.io.FileDescriptor; 206fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport java.io.PrintWriter; 216fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport java.util.HashMap; 226fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 236fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport com.android.location.provider.ProviderRequestUnbundled; 246fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 256fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.content.Context; 266fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.location.Location; 276fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.location.LocationListener; 286fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.location.LocationManager; 296fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.location.LocationRequest; 306fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.os.Bundle; 316fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.os.Looper; 326fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.os.SystemClock; 336fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.os.WorkSource; 346fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellyimport android.util.Log; 356fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 366fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pellypublic class FusionEngine implements LocationListener { 376fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public interface Callback { 386fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void reportLocation(Location location); 396fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 406fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 416fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final String TAG = "FusedLocation"; 426fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final String NETWORK = LocationManager.NETWORK_PROVIDER; 436fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final String GPS = LocationManager.GPS_PROVIDER; 446fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 456fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // threshold below which a location is considered stale enough 466fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // that we shouldn't use its bearing, altitude, speed etc 476fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final double WEIGHT_THRESHOLD = 0.5; 486fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // accuracy in meters at which a Location's weight is halved (compared to 0 accuracy) 496fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final double ACCURACY_HALFLIFE_M = 20.0; 506fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // age in seconds at which a Location's weight is halved (compared to 0 age) 516fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final double AGE_HALFLIFE_S = 60.0; 526fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 536fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final double ACCURACY_DECAY_CONSTANT_M = Math.log(2) / ACCURACY_HALFLIFE_M; 546fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static final double AGE_DECAY_CONSTANT_S = Math.log(2) / AGE_HALFLIFE_S; 556fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 566fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private final Context mContext; 576fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private final LocationManager mLocationManager; 586fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private final Looper mLooper; 596fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 606fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // all fields are only used on mLooper thread. except for in dump() which is not thread-safe 616fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private Callback mCallback; 626fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private Location mFusedLocation; 636fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private Location mGpsLocation; 646fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private Location mNetworkLocation; 656fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private double mNetworkWeight; 666fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private double mGpsWeight; 676fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 686fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private boolean mEnabled; 696fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private ProviderRequestUnbundled mRequest; 706fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 716fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private final HashMap<String, ProviderStats> mStats = new HashMap<String, ProviderStats>(); 726fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 736fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public FusionEngine(Context context, Looper looper) { 746fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mContext = context; 756fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 766fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkLocation = new Location(""); 776fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkLocation.setAccuracy(Float.MAX_VALUE); 786fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsLocation = new Location(""); 796fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsLocation.setAccuracy(Float.MAX_VALUE); 806fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mLooper = looper; 816fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 826fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mStats.put(GPS, new ProviderStats()); 836fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mStats.get(GPS).available = mLocationManager.isProviderEnabled(GPS); 846fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mStats.put(NETWORK, new ProviderStats()); 856fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mStats.get(NETWORK).available = mLocationManager.isProviderEnabled(NETWORK); 866fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 876fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 886fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void init(Callback callback) { 896fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly Log.i(TAG, "engine started (" + mContext.getPackageName() + ")"); 906fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mCallback = callback; 916fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 926fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 936fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** 946fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * Called to stop doing any work, and release all resources 956fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * This can happen when a better fusion engine is installed 966fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * in a different package, and this one is no longer needed. 976fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly * Called on mLooper thread 986fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly */ 996fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void deinit() { 1006fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mRequest = null; 1016fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly disable(); 1026fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")"); 1036fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1046fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1056fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private boolean isAvailable() { 1066fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly return mStats.get(GPS).available || mStats.get(NETWORK).available; 1076fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1086fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1096fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** Called on mLooper thread */ 1106fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void enable() { 1116fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mEnabled = true; 1126fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly updateRequirements(); 1136fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1146fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1156fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** Called on mLooper thread */ 1166fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void disable() { 1176fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mEnabled = false; 1186fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly updateRequirements(); 1196fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1206fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1216fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** Called on mLooper thread */ 12208ca1046fe4f1890f91241f8d082a024ef6cfd93Nick Pelly public void setRequest(ProviderRequestUnbundled request, WorkSource source) { 1236fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mRequest = request; 12408ca1046fe4f1890f91241f8d082a024ef6cfd93Nick Pelly mEnabled = request.getReportLocation(); 1256fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly updateRequirements(); 1266fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1276fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1286fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static class ProviderStats { 1296fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public boolean available; 1306fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public boolean requested; 1316fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public long requestTime; 1326fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public long minTime; 1336fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public long lastRequestTtff; 1346fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly @Override 1356fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public String toString() { 1366fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly StringBuilder s = new StringBuilder(); 1376fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append(available ? "AVAILABLE" : "UNAVAILABLE"); 1386fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append(requested ? " REQUESTED" : " ---"); 1396fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly return s.toString(); 1406fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1416fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1426fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1436fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private void enableProvider(String name, long minTime) { 1446fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly ProviderStats stats = mStats.get(name); 1456fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1466fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (!stats.requested) { 1476fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly stats.requestTime = SystemClock.elapsedRealtime(); 1486fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly stats.requested = true; 1496fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly stats.minTime = minTime; 1506fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper); 1516fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (stats.minTime != minTime) { 1526fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly stats.minTime = minTime; 1536fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper); 1546fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1556fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1566fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1576fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private void disableProvider(String name) { 1586fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly ProviderStats stats = mStats.get(name); 1596fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1606fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (stats.requested) { 1616fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly stats.requested = false; 1626fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mLocationManager.removeUpdates(this); //TODO GLOBAL 1636fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1646fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1656fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1666fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private void updateRequirements() { 1676fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (mEnabled == false || mRequest == null) { 1686fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mRequest = null; 1696fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly disableProvider(NETWORK); 1706fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly disableProvider(GPS); 1716fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly return; 1726fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1736fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1746fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly ProviderStats gpsStats = mStats.get(GPS); 1756fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly ProviderStats networkStats = mStats.get(NETWORK); 1766fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 1776fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly long networkInterval = Long.MAX_VALUE; 1786fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly long gpsInterval = Long.MAX_VALUE; 1796fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly for (LocationRequest request : mRequest.getLocationRequests()) { 1806fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly switch (request.getQuality()) { 1816fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly case LocationRequest.ACCURACY_FINE: 1826fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly case LocationRequest.POWER_HIGH: 1836fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (request.getInterval() < gpsInterval) { 1846fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly gpsInterval = request.getInterval(); 1856fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1866fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (request.getInterval() < networkInterval) { 1876fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly networkInterval = request.getInterval(); 1886fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1896fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly break; 1906fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly case LocationRequest.ACCURACY_BLOCK: 1916fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly case LocationRequest.ACCURACY_CITY: 1926fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly case LocationRequest.POWER_LOW: 1936fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (request.getInterval() < networkInterval) { 1946fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly networkInterval = request.getInterval(); 1956fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1966fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly break; 1976fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1986fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 1996fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2006fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (gpsInterval < Long.MAX_VALUE) { 2016fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly enableProvider(GPS, gpsInterval); 2026fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else { 2036fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly disableProvider(GPS); 2046fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2056fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (networkInterval < Long.MAX_VALUE) { 2066fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly enableProvider(NETWORK, networkInterval); 2076fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else { 2086fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly disableProvider(NETWORK); 2096fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2106fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2116fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2126fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static double weighAccuracy(Location loc) { 2136fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly double accuracy = loc.getAccuracy(); 2146fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly return Math.exp(-accuracy * ACCURACY_DECAY_CONSTANT_M); 2156fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2166fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2176fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private static double weighAge(Location loc) { 2186fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly long ageSeconds = SystemClock.elapsedRealtimeNano() - loc.getElapsedRealtimeNano(); 2196fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly ageSeconds /= 1000000000L; 2206fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (ageSeconds < 0) ageSeconds = 0; 2216fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly return Math.exp(-ageSeconds * AGE_DECAY_CONSTANT_S); 2226fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2236fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2246fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private double weigh(double gps, double network) { 2256fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly return (gps * mGpsWeight) + (network * mNetworkWeight); 2266fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2276fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2286fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private double weigh(double gps, double network, double wrapMin, double wrapMax) { 2296fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // apply aliasing 2306fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly double wrapWidth = wrapMax - wrapMin; 2316fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (gps - network > wrapWidth / 2) network += wrapWidth; 2326fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly else if (network - gps > wrapWidth / 2) gps += wrapWidth; 2336fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2346fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly double result = weigh(gps, network); 2356fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2366fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // remove aliasing 2376fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (result > wrapMax) result -= wrapWidth; 2386fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly return result; 2396fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2406fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2416fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly private void updateFusedLocation() { 2426fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // naive fusion 2436fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkWeight = weighAccuracy(mNetworkLocation) * weighAge(mNetworkLocation); 2446fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsWeight = weighAccuracy(mGpsLocation) * weighAge(mGpsLocation); 2456fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // scale mNetworkWeight and mGpsWeight so that they add to 1 2466fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly double totalWeight = mNetworkWeight + mGpsWeight; 2476fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkWeight /= totalWeight; 2486fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsWeight /= totalWeight; 2496fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2506fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly Location fused = new Location(LocationManager.FUSED_PROVIDER); 2516fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // fuse lat/long 2526fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // assumes the two locations are close enough that earth curvature doesn't matter 2536fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setLatitude(weigh(mGpsLocation.getLatitude(), mNetworkLocation.getLatitude())); 2546fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setLongitude(weigh(mGpsLocation.getLongitude(), mNetworkLocation.getLongitude(), 2556fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly -180.0, 180.0)); 2566fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2576fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // fused accuracy 2586fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly //TODO: use some real math instead of this crude fusion 2596fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // one suggestion is to fuse in a quadratic manner, eg 2606fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // sqrt(weigh(gpsAcc^2, netAcc^2)). 2616fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // another direction to explore is to consider the difference in the 2 2626fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // locations. If the component locations overlap, the fused accuracy is 2636fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // better than the component accuracies. If they are far apart, 2646fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // the fused accuracy is much worse. 2656fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setAccuracy((float)weigh(mGpsLocation.getAccuracy(), mNetworkLocation.getAccuracy())); 2666fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2676fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // fused time - now 2686fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setTime(System.currentTimeMillis()); 2696fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano()); 2706fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2716fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // fuse altitude 2726fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (mGpsLocation.hasAltitude() && !mNetworkLocation.hasAltitude() && 2736fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsWeight > WEIGHT_THRESHOLD) { 2746fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setAltitude(mGpsLocation.getAltitude()); // use GPS 2756fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (!mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude() && 2766fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkWeight > WEIGHT_THRESHOLD) { 2776fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setAltitude(mNetworkLocation.getAltitude()); // use Network 2786fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude()) { 2796fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setAltitude(weigh(mGpsLocation.getAltitude(), mNetworkLocation.getAltitude())); 2806fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2816fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2826fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // fuse speed 2836fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (mGpsLocation.hasSpeed() && !mNetworkLocation.hasSpeed() && 2846fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsWeight > WEIGHT_THRESHOLD) { 2856fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setSpeed(mGpsLocation.getSpeed()); // use GPS if its not too old 2866fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (!mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed() && 2876fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkWeight > WEIGHT_THRESHOLD) { 2886fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setSpeed(mNetworkLocation.getSpeed()); // use Network 2896fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed()) { 2906fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setSpeed((float)weigh(mGpsLocation.getSpeed(), mNetworkLocation.getSpeed())); 2916fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 2926fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 2936fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly // fuse bearing 2946fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (mGpsLocation.hasBearing() && !mNetworkLocation.hasBearing() && 2956fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsWeight > WEIGHT_THRESHOLD) { 2966fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setBearing(mGpsLocation.getBearing()); // use GPS if its not too old 2976fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (!mGpsLocation.hasBearing() && mNetworkLocation.hasBearing() && 2986fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkWeight > WEIGHT_THRESHOLD) { 2996fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setBearing(mNetworkLocation.getBearing()); // use Network 3006fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (mGpsLocation.hasBearing() && mNetworkLocation.hasBearing()) { 3016fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly fused.setBearing((float)weigh(mGpsLocation.getBearing(), mNetworkLocation.getBearing(), 3026fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 0.0, 360.0)); 3036fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 3046fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 30509016ab4dd056a16809419d612cb865a14980880Victoria Lease if (mNetworkLocation != null) { 30609016ab4dd056a16809419d612cb865a14980880Victoria Lease fused.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, mNetworkLocation); 30709016ab4dd056a16809419d612cb865a14980880Victoria Lease } 30809016ab4dd056a16809419d612cb865a14980880Victoria Lease 3096fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mFusedLocation = fused; 3106fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3116fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mCallback.reportLocation(mFusedLocation); 3126fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 3136fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3146fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** Called on mLooper thread */ 3156fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly @Override 3166fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void onLocationChanged(Location location) { 3176fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (GPS.equals(location.getProvider())) { 3186fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mGpsLocation = location; 3196fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly updateFusedLocation(); 3206fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } else if (NETWORK.equals(location.getProvider())) { 3216fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly mNetworkLocation = location; 3226fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly updateFusedLocation(); 3236fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 3246fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 3256fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3266fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** Called on mLooper thread */ 3276fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly @Override 3286fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void onStatusChanged(String provider, int status, Bundle extras) { } 3296fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3306fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** Called on mLooper thread */ 3316fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly @Override 3326fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void onProviderEnabled(String provider) { 3336fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly ProviderStats stats = mStats.get(provider); 3346fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (stats == null) return; 3356fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3366fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly stats.available = true; 3376fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 3386fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3396fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly /** Called on mLooper thread */ 3406fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly @Override 3416fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void onProviderDisabled(String provider) { 3426fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly ProviderStats stats = mStats.get(provider); 3436fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly if (stats == null) return; 3446fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3456fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly stats.available = false; 3466fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 3476fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly 3486fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3496fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly StringBuilder s = new StringBuilder(); 3506fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append("mEnabled=" + mEnabled).append(' ').append(mRequest).append('\n'); 3516fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append("fused=").append(mFusedLocation).append('\n'); 3526fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append(String.format("gps %.3f %s\n", mGpsWeight, mGpsLocation)); 3536fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append(" ").append(mStats.get(GPS)).append('\n'); 3546fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append(String.format("net %.3f %s\n", mNetworkWeight, mNetworkLocation)); 3556fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly s.append(" ").append(mStats.get(NETWORK)).append('\n'); 3566fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly pw.append(s); 3576fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly } 3586fa9ad4afcd762aea519ff61811386c23d18ddb2Nick Pelly} 359