Location overhaul, major commit.

Themes: Fused Location, Geofencing, LocationRequest.

API changes
o Fused location is always returned when asking for location by Criteria.
o Fused location is never returned as a LocationProvider object, nor returned
  as a provider String. This wouldn't make sense because the current API
  design assumes that LocationProvider's have fixed properties (accuracy, power
  etc).
o The fused location engine will tune itself based on the criteria passed
  by applications.
o Deprecate LocationProvider. Apps should use fused location (via Criteria
  class), instead of enumerating through LocationProvider objects. It is
  also over-engineered: designed for a world with a plethora of location
  providers that never materialized.
o The Criteria class is also over-engineered, with many methods that aren't
  currently used, but for now we won't deprecate them since they may have
  value in the future. It is now used to tune the fused location engine.
o Deprecate getBestProvider() and getProvider().
o Add getLastKnownLocation(Criteria), so we can return last known
  fused locations.
o Apps with only ACCESS_COARSE_LOCATION _can_ now use the GPS, but the location
  they receive will be fudged to a 1km radius. They can also use NETWORK
  and fused locatoins, which are fudged in the same way if necessary.
o Totally deprecate Criteria, in favor of LocationRequest.
  Criteria was designed to map QOS to a location provider. What we
  really need is to map QOS to _locations_.
  The death knell was the conflicting ACCURACY_ constants on
  Criteria, with values 1, 2, 3, 1, 2. Yes not a typo.
o Totally deprecate LocationProvider.
o Deprecate test/mock provider support. They require a named provider,
  which is a concept we are moving away from. We do not yet have a
  replacement, but I think its ok to deprecate since you also
  need to have 'allow mock locations' checked in developer settings.
  They will continue to work.
o Deprecate event codes associated with provider status. The fused
  provider is _always_ available.
o Introduce Geofence data object to provide an easier path fowards
  for polygons etc.

Implementation changes
o Fused implementation: incoming (GPS and NLP) location fixes are given
  a weight, that exponentially decays with respect to age and accuracy.
  The half-life of age is ~60 seconds, and the half-life of accuracy is
  ~20 meters. The fixes are weighted and combined to output a fused
  location.
o Move Fused Location impl into
  frameworks/base/packages/FusedLocation
o Refactor Fused Location behind the IProvider AIDL interface. This allow us
  to distribute newer versions of Fused Location in a new APK, at run-time.
o Introduce ServiceWatcher.java, to refactor code used for run-time upgrades of
  Fused Location, and the NLP.
o Fused Location is by default run in the system server (but can be moved to
  any process or pacakge, even at run-time).
o Plumb the Criteria requirements through to the Fused Location provider via
  ILocation.sendExtraCommand(). I re-used this interface to avoid modifying the
  ILocation interface, which would have broken run-time upgradability of the
  NLP.
o Switch the geofence manager to using fused location.
o Clean up 'adb shell dumpsys location' output.
o Introduce config_locationProviderPackageNames and
  config_overlay_locationProviderPackageNames to configure the default
  and overlay package names for Geocoder, NLP and FLP.
o Lots of misc cleanup.
o Improve location fudging. Apply random vector then quantize.
o Hide internal POJO's from clients of com.android.location.provider.jar
  (NLP and FLP). Introduce wrappers ProviderRequestUnbundled and
  ProviderPropertiesUnbundled.
o Introduce ProviderProperties to collapse all the provider accuracy/
  bearing/altitude/power plumbing (that is deprecated anyway).
o DELETE lots of code: DummyLocationProvider,
o Rename the (internal) LocationProvider to LocationProviderBase.
o Plumb pid, uid and packageName throughout
  LocationManagerService#Receiver to support future features.

TODO: The FLP and Geofencer have a lot of room to be more intelligent
TODO: Documentation
TODO: test test test

Change-Id: Iacefd2f176ed40ce1e23b090a164792aa8819c55
This commit is contained in:
Nick Pelly
2012-07-16 12:18:23 -07:00
parent c47f80f1ae
commit 6fa9ad4afc
42 changed files with 4398 additions and 3164 deletions

View File

@@ -0,0 +1,321 @@
/*
* 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 android.location;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.TimeUtils;
public final class LocationRequest implements Parcelable {
// QOS control
public static final int ACCURACY_FINE = 100; // ~1 meter
public static final int ACCURACY_BLOCK = 102; // ~100 meters
public static final int ACCURACY_CITY = 104; // ~10 km
public static final int POWER_NONE = 200;
public static final int POWER_LOW = 201;
public static final int POWER_HIGH = 203;
private int mQuality = POWER_LOW;
private long mFastestInterval = 6 * 1000; // 6 seconds
private long mInterval = 60 * 1000; // 1 minute
private long mExpireAt = Long.MAX_VALUE; // no expiry
private int mNumUpdates = Integer.MAX_VALUE; // no expiry
private float mSmallestDisplacement = 0.0f; // meters
private String mProvider = null; // for deprecated API's that explicitly request a provider
public static LocationRequest create() {
LocationRequest request = new LocationRequest();
return request;
}
/** @hide */
public static LocationRequest createFromDeprecatedProvider(String provider, long minTime,
float minDistance, boolean singleShot) {
if (minTime < 0) minTime = 0;
if (minDistance < 0) minDistance = 0;
int quality;
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
quality = POWER_NONE;
} else if (LocationManager.GPS_PROVIDER.equals(provider)) {
quality = ACCURACY_FINE;
} else {
quality = POWER_LOW;
}
LocationRequest request = new LocationRequest()
.setProvider(provider)
.setQuality(quality)
.setInterval(minTime)
.setFastestInterval(minTime)
.setSmallestDisplacement(minDistance);
if (singleShot) request.setNumUpdates(1);
return request;
}
/** @hide */
public static LocationRequest createFromDeprecatedCriteria(Criteria criteria, long minTime,
float minDistance, boolean singleShot) {
if (minTime < 0) minTime = 0;
if (minDistance < 0) minDistance = 0;
int quality;
switch (criteria.getAccuracy()) {
case Criteria.ACCURACY_COARSE:
quality = ACCURACY_BLOCK;
break;
case Criteria.ACCURACY_FINE:
quality = ACCURACY_FINE;
break;
default: {
switch (criteria.getPowerRequirement()) {
case Criteria.POWER_HIGH:
quality = POWER_HIGH;
default:
quality = POWER_LOW;
}
}
}
LocationRequest request = new LocationRequest()
.setQuality(quality)
.setInterval(minTime)
.setFastestInterval(minTime)
.setSmallestDisplacement(minDistance);
if (singleShot) request.setNumUpdates(1);
return request;
}
/** @hide */
public LocationRequest() { }
public LocationRequest setQuality(int quality) {
checkQuality(quality);
mQuality = quality;
return this;
}
public int getQuality() {
return mQuality;
}
public LocationRequest setInterval(long millis) {
checkInterval(millis);
mInterval = millis;
return this;
}
public long getInterval() {
return mInterval;
}
public LocationRequest setFastestInterval(long millis) {
checkInterval(millis);
mFastestInterval = millis;
return this;
}
public long getFastestInterval() {
return mFastestInterval;
}
public LocationRequest setExpireIn(long millis) {
mExpireAt = millis + SystemClock.elapsedRealtime();
if (mExpireAt < 0) mExpireAt = 0;
return this;
}
public LocationRequest setExpireAt(long millis) {
mExpireAt = millis;
if (mExpireAt < 0) mExpireAt = 0;
return this;
}
public long getExpireAt() {
return mExpireAt;
}
public int getNumUpdates() {
return mNumUpdates;
}
/** @hide */
public void decrementNumUpdates() {
if (mNumUpdates != Integer.MAX_VALUE) {
mNumUpdates--;
}
if (mNumUpdates < 0) {
mNumUpdates = 0;
}
}
public LocationRequest setNumUpdates(int numUpdates) {
if (numUpdates < 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
mNumUpdates = numUpdates;
return this;
}
/** @hide */
public LocationRequest setProvider(String provider) {
checkProvider(provider);
mProvider = provider;
return this;
}
/** @hide */
public String getProvider() {
return mProvider;
}
/** @hide */
public LocationRequest setSmallestDisplacement(float meters) {
checkDisplacement(meters);
mSmallestDisplacement = meters;
return this;
}
/** @hide */
public float getSmallestDisplacement() {
return mSmallestDisplacement;
}
/** @hide */
public LocationRequest applyCoarsePermissionRestrictions() {
switch (mQuality) {
case ACCURACY_FINE:
mQuality = ACCURACY_BLOCK;
break;
}
// cap fastest interval to 6 seconds
if (mFastestInterval < 6 * 1000) mFastestInterval = 6 * 1000;
// cap requested interval to 1 minute
if (mInterval < 60 * 1000) mInterval = 60 * 1000;
return this;
}
private static void checkInterval(long millis) {
if (millis < 0) {
throw new IllegalArgumentException("invalid interval: " + millis);
}
}
private static void checkQuality(int quality) {
switch (quality) {
case ACCURACY_FINE:
case ACCURACY_BLOCK:
case ACCURACY_CITY:
case POWER_NONE:
case POWER_LOW:
case POWER_HIGH:
break;
default:
throw new IllegalArgumentException("invalid quality: " + quality);
}
}
private static void checkDisplacement(float meters) {
if (meters < 0.0f) {
throw new IllegalArgumentException("invalid displacement: " + meters);
}
}
private static void checkProvider(String name) {
if (name == null) {
throw new IllegalArgumentException("invalid provider: " + name);
}
}
public static final Parcelable.Creator<LocationRequest> CREATOR =
new Parcelable.Creator<LocationRequest>() {
@Override
public LocationRequest createFromParcel(Parcel in) {
LocationRequest request = new LocationRequest();
request.setQuality(in.readInt());
request.setFastestInterval(in.readLong());
request.setInterval(in.readLong());
request.setExpireAt(in.readLong());
request.setNumUpdates(in.readInt());
request.setSmallestDisplacement(in.readFloat());
String provider = in.readString();
if (provider != null) request.setProvider(provider);
return request;
}
@Override
public LocationRequest[] newArray(int size) {
return new LocationRequest[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mQuality);
parcel.writeLong(mFastestInterval);
parcel.writeLong(mInterval);
parcel.writeLong(mExpireAt);
parcel.writeInt(mNumUpdates);
parcel.writeFloat(mSmallestDisplacement);
parcel.writeString(mProvider);
}
/** @hide */
public static String qualityToString(int quality) {
switch (quality) {
case ACCURACY_FINE:
return "ACCURACY_FINE";
case ACCURACY_BLOCK:
return "ACCURACY_BLOCK";
case ACCURACY_CITY:
return "ACCURACY_CITY";
case POWER_NONE:
return "POWER_NONE";
case POWER_LOW:
return "POWER_LOW";
case POWER_HIGH:
return "POWER_HIGH";
default:
return "???";
}
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("Request[").append(qualityToString(mQuality));
if (mProvider != null) s.append(' ').append(mProvider);
if (mQuality != POWER_NONE) {
s.append(" requested=");
TimeUtils.formatDuration(mInterval, s);
}
s.append(" fastest=");
TimeUtils.formatDuration(mFastestInterval, s);
if (mExpireAt != Long.MAX_VALUE) {
long expireIn = mExpireAt - SystemClock.elapsedRealtime();
s.append(" expireIn=");
TimeUtils.formatDuration(expireIn, s);
}
if (mNumUpdates != Integer.MAX_VALUE){
s.append(" num=").append(mNumUpdates);
}
s.append(']');
return s.toString();
}
}