diff options
Diffstat (limited to 'packages')
8 files changed, 790 insertions, 0 deletions
diff --git a/packages/FusedLocation/Android.mk b/packages/FusedLocation/Android.mk new file mode 100644 index 0000000..318782f --- /dev/null +++ b/packages/FusedLocation/Android.mk @@ -0,0 +1,27 @@ +# Copyright (C) 2012 The Android Open Source Project +# +# 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_JAVA_LIBRARIES := com.android.location.provider + +LOCAL_PACKAGE_NAME := FusedLocation +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml new file mode 100644 index 0000000..4c57401 --- /dev/null +++ b/packages/FusedLocation/AndroidManifest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (c) 2012 Google Inc. + * + * 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. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.location.fused" + coreApp="true"> + + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" /> + + <application + android:label="@string/app_label" + android:process="system"> + + <uses-library android:name="com.android.location.provider" /> + + <!-- Fused Location Service that LocationManagerService binds to. + LocationManagerService will bind to the service with the highest + version. --> + <service android:name="com.android.location.fused.FusedLocationService" + android:exported="true" + android:permission="android.permission.WRITE_SECURE_SETTINGS" > + <intent-filter> + <action android:name="com.android.location.service.FusedLocationProvider" /> + </intent-filter> + <meta-data android:name="version" android:value="1" /> + </service> + </application> +</manifest> diff --git a/packages/FusedLocation/MODULE_LICENSE_APACHE2 b/packages/FusedLocation/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/packages/FusedLocation/MODULE_LICENSE_APACHE2 diff --git a/packages/FusedLocation/NOTICE b/packages/FusedLocation/NOTICE new file mode 100644 index 0000000..33ff961 --- /dev/null +++ b/packages/FusedLocation/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2012, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/packages/FusedLocation/res/values/strings.xml b/packages/FusedLocation/res/values/strings.xml new file mode 100644 index 0000000..5b78e39 --- /dev/null +++ b/packages/FusedLocation/res/values/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name of the application. [CHAR LIMIT=35] --> + <string name="app_label">Fused Location</string> +</resources> diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java new file mode 100644 index 0000000..45f05f3 --- /dev/null +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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 com.android.location.fused; + + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +import com.android.location.provider.LocationProviderBase; +import com.android.location.provider.ProviderPropertiesUnbundled; +import com.android.location.provider.ProviderRequestUnbundled; + +import android.content.Context; +import android.location.Criteria; +import android.location.LocationProvider; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.WorkSource; + +public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback { + private static final String TAG = "FusedLocationProvider"; + + private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create( + false, false, false, false, true, true, true, Criteria.POWER_LOW, + Criteria.ACCURACY_FINE); + + private static final int MSG_ENABLE = 1; + private static final int MSG_DISABLE = 2; + private static final int MSG_SET_REQUEST = 3; + + private final Context mContext; + private final FusionEngine mEngine; + + private static class RequestWrapper { + public ProviderRequestUnbundled request; + public WorkSource source; + public RequestWrapper(ProviderRequestUnbundled request, WorkSource source) { + this.request = request; + this.source = source; + } + } + + public FusedLocationProvider(Context context) { + super(TAG, PROPERTIES); + mContext = context; + mEngine = new FusionEngine(context, Looper.myLooper()); + } + + /** + * For serializing requests to mEngine. + */ + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ENABLE: + mEngine.init(FusedLocationProvider.this); + break; + case MSG_DISABLE: + mEngine.deinit(); + break; + case MSG_SET_REQUEST: + { + RequestWrapper wrapper = (RequestWrapper) msg.obj; + mEngine.setRequirements(wrapper.request, wrapper.source); + break; + } + } + } + }; + + @Override + public void onEnable() { + mHandler.sendEmptyMessage(MSG_ENABLE); + } + + @Override + public void onDisable() { + mHandler.sendEmptyMessage(MSG_DISABLE); + } + + @Override + public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) { + mHandler.obtainMessage(MSG_SET_REQUEST, new RequestWrapper(request, source)); + } + + @Override + public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) { + // perform synchronously + mEngine.dump(fd, pw, args); + } + + @Override + public int onGetStatus(Bundle extras) { + return LocationProvider.AVAILABLE; + } + + @Override + public long onGetStatusUpdateTime() { + return 0; + } +} diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java new file mode 100644 index 0000000..509c010 --- /dev/null +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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 com.android.location.fused; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class FusedLocationService extends Service { + private FusedLocationProvider mProvider; + + @Override + public IBinder onBind(Intent intent) { + if (mProvider == null) { + mProvider = new FusedLocationProvider(getApplicationContext()); + } + return mProvider.getBinder(); + } + + @Override + public boolean onUnbind(Intent intent) { + // make sure to stop performing work + if (mProvider != null) { + mProvider.onDisable(); + } + return false; + } + + @Override + public void onDestroy() { + mProvider = null; + } +} diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java new file mode 100644 index 0000000..f4f87a8 --- /dev/null +++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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 com.android.location.fused; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.HashMap; + +import com.android.location.provider.ProviderRequestUnbundled; + + +import android.content.Context; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationRequest; +import android.os.Bundle; +import android.os.Looper; +import android.os.SystemClock; +import android.os.WorkSource; +import android.util.Log; + +public class FusionEngine implements LocationListener { + public interface Callback { + public void reportLocation(Location location); + } + + private static final String TAG = "FusedLocation"; + private static final String NETWORK = LocationManager.NETWORK_PROVIDER; + private static final String GPS = LocationManager.GPS_PROVIDER; + + // threshold below which a location is considered stale enough + // that we shouldn't use its bearing, altitude, speed etc + private static final double WEIGHT_THRESHOLD = 0.5; + // accuracy in meters at which a Location's weight is halved (compared to 0 accuracy) + private static final double ACCURACY_HALFLIFE_M = 20.0; + // age in seconds at which a Location's weight is halved (compared to 0 age) + private static final double AGE_HALFLIFE_S = 60.0; + + private static final double ACCURACY_DECAY_CONSTANT_M = Math.log(2) / ACCURACY_HALFLIFE_M; + private static final double AGE_DECAY_CONSTANT_S = Math.log(2) / AGE_HALFLIFE_S; + + private final Context mContext; + private final LocationManager mLocationManager; + private final Looper mLooper; + + // all fields are only used on mLooper thread. except for in dump() which is not thread-safe + private Callback mCallback; + private Location mFusedLocation; + private Location mGpsLocation; + private Location mNetworkLocation; + private double mNetworkWeight; + private double mGpsWeight; + + private boolean mEnabled; + private ProviderRequestUnbundled mRequest; + + private final HashMap<String, ProviderStats> mStats = new HashMap<String, ProviderStats>(); + + public FusionEngine(Context context, Looper looper) { + mContext = context; + mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + mNetworkLocation = new Location(""); + mNetworkLocation.setAccuracy(Float.MAX_VALUE); + mGpsLocation = new Location(""); + mGpsLocation.setAccuracy(Float.MAX_VALUE); + mLooper = looper; + + mStats.put(GPS, new ProviderStats()); + mStats.get(GPS).available = mLocationManager.isProviderEnabled(GPS); + mStats.put(NETWORK, new ProviderStats()); + mStats.get(NETWORK).available = mLocationManager.isProviderEnabled(NETWORK); + } + + public void init(Callback callback) { + Log.i(TAG, "engine started (" + mContext.getPackageName() + ")"); + mCallback = callback; + } + + /** + * Called to stop doing any work, and release all resources + * This can happen when a better fusion engine is installed + * in a different package, and this one is no longer needed. + * Called on mLooper thread + */ + public void deinit() { + mRequest = null; + disable(); + Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")"); + } + + private boolean isAvailable() { + return mStats.get(GPS).available || mStats.get(NETWORK).available; + } + + /** Called on mLooper thread */ + public void enable() { + mEnabled = true; + updateRequirements(); + } + + /** Called on mLooper thread */ + public void disable() { + mEnabled = false; + updateRequirements(); + } + + /** Called on mLooper thread */ + public void setRequirements(ProviderRequestUnbundled request, WorkSource source) { + mRequest = request; + mEnabled = true; + updateRequirements(); + } + + private static class ProviderStats { + public boolean available; + public boolean requested; + public long requestTime; + public long minTime; + public long lastRequestTtff; + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + s.append(available ? "AVAILABLE" : "UNAVAILABLE"); + s.append(requested ? " REQUESTED" : " ---"); + return s.toString(); + } + } + + private void enableProvider(String name, long minTime) { + ProviderStats stats = mStats.get(name); + + if (!stats.requested) { + stats.requestTime = SystemClock.elapsedRealtime(); + stats.requested = true; + stats.minTime = minTime; + mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper); + } else if (stats.minTime != minTime) { + stats.minTime = minTime; + mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper); + } + } + + private void disableProvider(String name) { + ProviderStats stats = mStats.get(name); + + if (stats.requested) { + stats.requested = false; + mLocationManager.removeUpdates(this); //TODO GLOBAL + } + } + + private void updateRequirements() { + if (mEnabled == false || mRequest == null) { + mRequest = null; + disableProvider(NETWORK); + disableProvider(GPS); + return; + } + + ProviderStats gpsStats = mStats.get(GPS); + ProviderStats networkStats = mStats.get(NETWORK); + + long networkInterval = Long.MAX_VALUE; + long gpsInterval = Long.MAX_VALUE; + for (LocationRequest request : mRequest.getLocationRequests()) { + switch (request.getQuality()) { + case LocationRequest.ACCURACY_FINE: + case LocationRequest.POWER_HIGH: + if (request.getInterval() < gpsInterval) { + gpsInterval = request.getInterval(); + } + if (request.getInterval() < networkInterval) { + networkInterval = request.getInterval(); + } + break; + case LocationRequest.ACCURACY_BLOCK: + case LocationRequest.ACCURACY_CITY: + case LocationRequest.POWER_LOW: + if (request.getInterval() < networkInterval) { + networkInterval = request.getInterval(); + } + break; + } + } + + if (gpsInterval < Long.MAX_VALUE) { + enableProvider(GPS, gpsInterval); + } else { + disableProvider(GPS); + } + if (networkInterval < Long.MAX_VALUE) { + enableProvider(NETWORK, networkInterval); + } else { + disableProvider(NETWORK); + } + } + + private static double weighAccuracy(Location loc) { + double accuracy = loc.getAccuracy(); + return Math.exp(-accuracy * ACCURACY_DECAY_CONSTANT_M); + } + + private static double weighAge(Location loc) { + long ageSeconds = SystemClock.elapsedRealtimeNano() - loc.getElapsedRealtimeNano(); + ageSeconds /= 1000000000L; + if (ageSeconds < 0) ageSeconds = 0; + return Math.exp(-ageSeconds * AGE_DECAY_CONSTANT_S); + } + + private double weigh(double gps, double network) { + return (gps * mGpsWeight) + (network * mNetworkWeight); + } + + private double weigh(double gps, double network, double wrapMin, double wrapMax) { + // apply aliasing + double wrapWidth = wrapMax - wrapMin; + if (gps - network > wrapWidth / 2) network += wrapWidth; + else if (network - gps > wrapWidth / 2) gps += wrapWidth; + + double result = weigh(gps, network); + + // remove aliasing + if (result > wrapMax) result -= wrapWidth; + return result; + } + + private void updateFusedLocation() { + // naive fusion + mNetworkWeight = weighAccuracy(mNetworkLocation) * weighAge(mNetworkLocation); + mGpsWeight = weighAccuracy(mGpsLocation) * weighAge(mGpsLocation); + // scale mNetworkWeight and mGpsWeight so that they add to 1 + double totalWeight = mNetworkWeight + mGpsWeight; + mNetworkWeight /= totalWeight; + mGpsWeight /= totalWeight; + + Location fused = new Location(LocationManager.FUSED_PROVIDER); + // fuse lat/long + // assumes the two locations are close enough that earth curvature doesn't matter + fused.setLatitude(weigh(mGpsLocation.getLatitude(), mNetworkLocation.getLatitude())); + fused.setLongitude(weigh(mGpsLocation.getLongitude(), mNetworkLocation.getLongitude(), + -180.0, 180.0)); + + // fused accuracy + //TODO: use some real math instead of this crude fusion + // one suggestion is to fuse in a quadratic manner, eg + // sqrt(weigh(gpsAcc^2, netAcc^2)). + // another direction to explore is to consider the difference in the 2 + // locations. If the component locations overlap, the fused accuracy is + // better than the component accuracies. If they are far apart, + // the fused accuracy is much worse. + fused.setAccuracy((float)weigh(mGpsLocation.getAccuracy(), mNetworkLocation.getAccuracy())); + + // fused time - now + fused.setTime(System.currentTimeMillis()); + fused.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano()); + + // fuse altitude + if (mGpsLocation.hasAltitude() && !mNetworkLocation.hasAltitude() && + mGpsWeight > WEIGHT_THRESHOLD) { + fused.setAltitude(mGpsLocation.getAltitude()); // use GPS + } else if (!mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude() && + mNetworkWeight > WEIGHT_THRESHOLD) { + fused.setAltitude(mNetworkLocation.getAltitude()); // use Network + } else if (mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude()) { + fused.setAltitude(weigh(mGpsLocation.getAltitude(), mNetworkLocation.getAltitude())); + } + + // fuse speed + if (mGpsLocation.hasSpeed() && !mNetworkLocation.hasSpeed() && + mGpsWeight > WEIGHT_THRESHOLD) { + fused.setSpeed(mGpsLocation.getSpeed()); // use GPS if its not too old + } else if (!mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed() && + mNetworkWeight > WEIGHT_THRESHOLD) { + fused.setSpeed(mNetworkLocation.getSpeed()); // use Network + } else if (mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed()) { + fused.setSpeed((float)weigh(mGpsLocation.getSpeed(), mNetworkLocation.getSpeed())); + } + + // fuse bearing + if (mGpsLocation.hasBearing() && !mNetworkLocation.hasBearing() && + mGpsWeight > WEIGHT_THRESHOLD) { + fused.setBearing(mGpsLocation.getBearing()); // use GPS if its not too old + } else if (!mGpsLocation.hasBearing() && mNetworkLocation.hasBearing() && + mNetworkWeight > WEIGHT_THRESHOLD) { + fused.setBearing(mNetworkLocation.getBearing()); // use Network + } else if (mGpsLocation.hasBearing() && mNetworkLocation.hasBearing()) { + fused.setBearing((float)weigh(mGpsLocation.getBearing(), mNetworkLocation.getBearing(), + 0.0, 360.0)); + } + + mFusedLocation = fused; + + mCallback.reportLocation(mFusedLocation); + } + + /** Called on mLooper thread */ + @Override + public void onLocationChanged(Location location) { + if (GPS.equals(location.getProvider())) { + mGpsLocation = location; + updateFusedLocation(); + } else if (NETWORK.equals(location.getProvider())) { + mNetworkLocation = location; + updateFusedLocation(); + } + } + + /** Called on mLooper thread */ + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { } + + /** Called on mLooper thread */ + @Override + public void onProviderEnabled(String provider) { + ProviderStats stats = mStats.get(provider); + if (stats == null) return; + + stats.available = true; + } + + /** Called on mLooper thread */ + @Override + public void onProviderDisabled(String provider) { + ProviderStats stats = mStats.get(provider); + if (stats == null) return; + + stats.available = false; + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + StringBuilder s = new StringBuilder(); + s.append("mEnabled=" + mEnabled).append(' ').append(mRequest).append('\n'); + s.append("fused=").append(mFusedLocation).append('\n'); + s.append(String.format("gps %.3f %s\n", mGpsWeight, mGpsLocation)); + s.append(" ").append(mStats.get(GPS)).append('\n'); + s.append(String.format("net %.3f %s\n", mNetworkWeight, mNetworkLocation)); + s.append(" ").append(mStats.get(NETWORK)).append('\n'); + pw.append(s); + } +} |