Skip to content

Commit 74fa7ea

Browse files
author
Nick Pelly
committed
Improve coarse locations.
Fix a couple of bugs, and modify the behavior of the random offset. The random offset now slowly changes over time, to mitigate against applications averaging out the offset over time while at a grid boundary. Change-Id: Iecffff29145b8c2b30d1eca1662cf9d3e8cff756
1 parent 08ca104 commit 74fa7ea

File tree

3 files changed

+335
-135
lines changed

3 files changed

+335
-135
lines changed

location/java/android/location/LocationRequest.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -195,20 +195,6 @@ public float getSmallestDisplacement() {
195195
return mSmallestDisplacement;
196196
}
197197

198-
/** @hide */
199-
public LocationRequest applyCoarsePermissionRestrictions() {
200-
switch (mQuality) {
201-
case ACCURACY_FINE:
202-
mQuality = ACCURACY_BLOCK;
203-
break;
204-
}
205-
// cap fastest interval to 6 seconds
206-
if (mFastestInterval < 6 * 1000) mFastestInterval = 6 * 1000;
207-
// cap requested interval to 1 minute
208-
if (mInterval < 60 * 1000) mInterval = 60 * 1000;
209-
return this;
210-
}
211-
212198
private static void checkInterval(long millis) {
213199
if (millis < 0) {
214200
throw new IllegalArgumentException("invalid interval: " + millis);

services/java/com/android/server/LocationManagerService.java

Lines changed: 25 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import android.os.IBinder;
4848
import android.os.Looper;
4949
import android.os.Message;
50-
import android.os.Parcelable;
5150
import android.os.PowerManager;
5251
import android.os.Process;
5352
import android.os.RemoteException;
@@ -64,6 +63,7 @@
6463
import com.android.server.location.GeocoderProxy;
6564
import com.android.server.location.GeofenceManager;
6665
import com.android.server.location.GpsLocationProvider;
66+
import com.android.server.location.LocationFudger;
6767
import com.android.server.location.LocationProviderInterface;
6868
import com.android.server.location.LocationProviderProxy;
6969
import com.android.server.location.MockProvider;
@@ -110,19 +110,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs
110110

111111
private static final int MSG_LOCATION_CHANGED = 1;
112112

113-
// Accuracy in meters above which a location is considered coarse
114-
private static final double COARSE_ACCURACY_M = 100.0;
115-
private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
116-
117-
private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
118-
119-
/**
120-
* Maximum latitude of 1 meter from the pole.
121-
* This keeps cosine(MAX_LATITUDE) to a non-zero value;
122-
*/
123-
private static final double MAX_LATITUDE =
124-
90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
125-
126113
// Location Providers may sometimes deliver location updates
127114
// slightly faster that requested - provide grace period so
128115
// we don't unnecessarily filter events that are otherwise on
@@ -137,6 +124,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs
137124
private final Object mLock = new Object();
138125

139126
// --- fields below are final after init() ---
127+
private LocationFudger mLocationFudger;
140128
private GeofenceManager mGeofenceManager;
141129
private PowerManager.WakeLock mWakeLock;
142130
private PackageManager mPackageManager;
@@ -221,6 +209,7 @@ private void init() {
221209
loadProvidersLocked();
222210
}
223211
mGeofenceManager = new GeofenceManager(mContext);
212+
mLocationFudger = new LocationFudger();
224213

225214
// Register for Network (Wifi or Mobile) updates
226215
IntentFilter filter = new IntentFilter();
@@ -907,7 +896,22 @@ private String checkPermissionAndRequest(LocationRequest request) {
907896
String perm = checkPermission();
908897

909898
if (ACCESS_COARSE_LOCATION.equals(perm)) {
910-
request.applyCoarsePermissionRestrictions();
899+
switch (request.getQuality()) {
900+
case LocationRequest.ACCURACY_FINE:
901+
request.setQuality(LocationRequest.ACCURACY_BLOCK);
902+
break;
903+
case LocationRequest.POWER_HIGH:
904+
request.setQuality(LocationRequest.POWER_LOW);
905+
break;
906+
}
907+
// throttle fastest interval
908+
if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
909+
request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
910+
}
911+
}
912+
// throttle interval if its faster than the fastest interval
913+
if (request.getInterval () < request.getFastestInterval()) {
914+
request.setInterval(request.getFastestInterval());
911915
}
912916
return perm;
913917
}
@@ -1075,7 +1079,7 @@ public Location getLastLocation(LocationRequest request) {
10751079
if (ACCESS_FINE_LOCATION.equals(perm)) {
10761080
return location;
10771081
} else {
1078-
return getCoarseLocationExtra(location);
1082+
return mLocationFudger.getOrCreate(location);
10791083
}
10801084
}
10811085
}
@@ -1291,11 +1295,8 @@ private void handleLocationChangedLocked(Location location, boolean passive) {
12911295
LocationProviderInterface p = mProvidersByName.get(provider);
12921296
if (p == null) return;
12931297

1294-
// Add the coarse location as an extra, if not already present
1295-
Location coarse = getCoarseLocationExtra(location);
1296-
if (coarse == null) {
1297-
coarse = addCoarseLocationExtra(location);
1298-
}
1298+
// Add the coarse location as an extra
1299+
Location coarse = mLocationFudger.getOrCreate(location);
12991300

13001301
// Update last known locations
13011302
Location lastLocation = mLastLocation.get(provider);
@@ -1660,106 +1661,6 @@ public void clearTestProviderStatus(String provider) {
16601661
}
16611662
}
16621663

1663-
private static double wrapLatitude(double lat) {
1664-
if (lat > MAX_LATITUDE) lat = MAX_LATITUDE;
1665-
if (lat < -MAX_LATITUDE) lat = -MAX_LATITUDE;
1666-
return lat;
1667-
}
1668-
1669-
private static double wrapLongitude(double lon) {
1670-
if (lon >= 180.0) lon -= 360.0;
1671-
if (lon < -180.0) lon += 360.0;
1672-
return lon;
1673-
}
1674-
1675-
private static double distanceToDegreesLatitude(double distance) {
1676-
return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
1677-
}
1678-
1679-
/**
1680-
* Requires latitude since longitudinal distances change with distance from equator.
1681-
*/
1682-
private static double distanceToDegreesLongitude(double distance, double lat) {
1683-
return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(lat);
1684-
}
1685-
1686-
/**
1687-
* Fudge a location into a coarse location.
1688-
* <p>Add a random offset, then quantize the result (snap-to-grid).
1689-
* Random offsets alone can be low-passed pretty easily.
1690-
* Snap-to-grid on its own is excellent unless you are sitting on a
1691-
* grid boundary and bouncing between quantizations.
1692-
* The combination is quite hard to reverse engineer.
1693-
* <p>The random offset used is smaller than the goal accuracy
1694-
* ({@link #COARSE_ACCURACY_M}), in order to give relatively stable
1695-
* results after quantization.
1696-
*/
1697-
private static Location createCoarse(Location fine) {
1698-
Location coarse = new Location(fine);
1699-
1700-
coarse.removeBearing();
1701-
coarse.removeSpeed();
1702-
coarse.removeAltitude();
1703-
1704-
double lat = coarse.getLatitude();
1705-
double lon = coarse.getLongitude();
1706-
1707-
// wrap
1708-
lat = wrapLatitude(lat);
1709-
lon = wrapLongitude(lon);
1710-
1711-
if (coarse.getAccuracy() < COARSE_ACCURACY_M / 2) {
1712-
// apply a random offset
1713-
double fudgeDistance = COARSE_ACCURACY_M / 2.0 - coarse.getAccuracy();
1714-
lat += (Math.random() - 0.5) * distanceToDegreesLatitude(fudgeDistance);
1715-
lon += (Math.random() - 0.5) * distanceToDegreesLongitude(fudgeDistance, lat);
1716-
}
1717-
1718-
// wrap
1719-
lat = wrapLatitude(lat);
1720-
lon = wrapLongitude(lon);
1721-
1722-
// quantize (snap-to-grid)
1723-
double latGranularity = distanceToDegreesLatitude(COARSE_ACCURACY_M);
1724-
double lonGranularity = distanceToDegreesLongitude(COARSE_ACCURACY_M, lat);
1725-
long latQuantized = Math.round(lat / latGranularity);
1726-
long lonQuantized = Math.round(lon / lonGranularity);
1727-
lat = latQuantized * latGranularity;
1728-
lon = lonQuantized * lonGranularity;
1729-
1730-
// wrap again
1731-
lat = wrapLatitude(lat);
1732-
lon = wrapLongitude(lon);
1733-
1734-
// apply
1735-
coarse.setLatitude(lat);
1736-
coarse.setLongitude(lon);
1737-
coarse.setAccuracy((float)COARSE_ACCURACY_M);
1738-
1739-
return coarse;
1740-
}
1741-
1742-
1743-
private static Location getCoarseLocationExtra(Location location) {
1744-
Bundle extras = location.getExtras();
1745-
if (extras == null) return null;
1746-
Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
1747-
if (parcel == null) return null;
1748-
if (!(parcel instanceof Location)) return null;
1749-
Location coarse = (Location) parcel;
1750-
if (coarse.getAccuracy() < COARSE_ACCURACY_M) return null;
1751-
return coarse;
1752-
}
1753-
1754-
private static Location addCoarseLocationExtra(Location location) {
1755-
Bundle extras = location.getExtras();
1756-
if (extras == null) extras = new Bundle();
1757-
Location coarse = createCoarse(location);
1758-
extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
1759-
location.setExtras(extras);
1760-
return coarse;
1761-
}
1762-
17631664
private void log(String log) {
17641665
if (Log.isLoggable(TAG, Log.VERBOSE)) {
17651666
Slog.d(TAG, log);
@@ -1819,6 +1720,9 @@ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
18191720
}
18201721
}
18211722

1723+
pw.append(" fudger: ");
1724+
mLocationFudger.dump(fd, pw, args);
1725+
18221726
if (args.length > 0 && "short".equals(args[0])) {
18231727
return;
18241728
}

0 commit comments

Comments
 (0)