summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/FusedLocation/Android.mk27
-rw-r--r--packages/FusedLocation/AndroidManifest.xml45
-rw-r--r--packages/FusedLocation/MODULE_LICENSE_APACHE20
-rw-r--r--packages/FusedLocation/NOTICE190
-rw-r--r--packages/FusedLocation/res/values/strings.xml5
-rw-r--r--packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java118
-rw-r--r--packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java50
-rw-r--r--packages/FusedLocation/src/com/android/location/fused/FusionEngine.java355
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);
+ }
+}