New Location Manager APIs for Criteria based requests and single shot mode.

Use MS-Assisted mode for single shot GPS fixes if it is supported.

Add finer grained control over accuracy to the android.location.Criteria class
and location criteria logic from LocationManager to LocationManagerService

Change-Id: I156b1f6c6a45d255c87ff917cf3e9726a6d7a75b
Signed-off-by: Mike Lockwood <lockwood@android.com>
This commit is contained in:
Mike Lockwood
2010-04-01 08:10:09 -07:00
parent 4979601f88
commit 03ca216ac1
13 changed files with 970 additions and 347 deletions

View File

@ -57,9 +57,81 @@ public class Criteria implements Parcelable {
*/
public static final int ACCURACY_COARSE = 2;
private int mAccuracy = NO_REQUIREMENT;
/**
* A constant indicating a low location accuracy requirement
* - may be used for horizontal, altitude, speed or bearing accuracy.
* For horizontal and vertical position this corresponds to an accuracy
* greater than 500 meters. For speed and bearing, this corresponds
* to greater than 5 meters/second velocity and 10 degrees for bearing.
* {@hide}
*/
public static final int ACCURACY_LOW = 1;
/**
* A constant indicating a medium accuracy requirement
* - may be used for horizontal, altitude, speed or bearing accuracy.
* For horizontal position this corresponds to an accuracy of between
* 100 and 500 meters, and between 200 and 500 meters for vertical accuracy.
* For speed and bearing, this corresponds to 1 meter/second to 5 meters/second
* velocity and and between 5 and 10 degrees for bearing.
* {@hide}
*/
public static final int ACCURACY_MEDIUM = 2;
/**
* a constant indicating a high accuracy requirement
* - may be used for horizontal, altitude, speed or bearing accuracy.
* For horizontal and vertical position this corresponds to an accuracy
* less than 100 meters. For speed and bearing, this corresponds
* to less 1 meter/second velocity less than 5 degrees for bearing.
* {@hide}
*/
public static final int ACCURACY_HIGH = 3;
/**
* a constant indicating the best accuracy that is available for any
* location provider available
* - may be used for horizontal, altitude, speed or bearing accuracy.
* {@hide}
*/
public static final int ACCURACY_BEST = 4;
/**
* A constant indicating horizontal accuracy has the top priority
* {@hide}
*/
public static final int HORIZONTAL_ACCURACY_PRIORITY = 1;
/**
* A constant indicating altitude accuracy has the top priority
* {@hide}
*/
public static final int VERTICAL_ACCURACY_PRIORITY = 2;
/**
* A constant indicating speed accuracy has the top priority
* {@hide}
*/
public static final int SPEED_ACCURACY_PRIORITY = 3;
/**
* A constant indicating bearing accuracy has the top priority
* {@hide}
*/
public static final int BEARING_ACCURACY_PRIORITY = 4;
/**
* A constant indicating power requirement has the top priority
* {@hide}
*/
public static final int POWER_REQUIREMENT_PRIORITY = 5;
private int mHorizontalAccuracy = NO_REQUIREMENT;
private int mVerticalAccuracy = NO_REQUIREMENT;
private int mSpeedAccuracy = NO_REQUIREMENT;
private int mBearingAccuracy = NO_REQUIREMENT;
private int mPriority = HORIZONTAL_ACCURACY_PRIORITY;
private int mPowerRequirement = NO_REQUIREMENT;
// private int mPreferredResponseTime = NO_REQUIREMENT;
private boolean mAltitudeRequired = false;
private boolean mBearingRequired = false;
private boolean mSpeedRequired = false;
@ -77,29 +149,172 @@ public class Criteria implements Parcelable {
* Constructs a new Criteria object that is a copy of the given criteria.
*/
public Criteria(Criteria criteria) {
mAccuracy = criteria.mAccuracy;
mHorizontalAccuracy = criteria.mHorizontalAccuracy;
mVerticalAccuracy = criteria.mVerticalAccuracy;
mSpeedAccuracy = criteria.mSpeedAccuracy;
mBearingAccuracy = criteria.mBearingAccuracy;
mPriority = criteria.mPriority;
mPowerRequirement = criteria.mPowerRequirement;
// mPreferredResponseTime = criteria.mPreferredResponseTime;
mAltitudeRequired = criteria.mAltitudeRequired;
mBearingRequired = criteria.mBearingRequired;
mSpeedRequired = criteria.mSpeedRequired;
mCostAllowed = criteria.mCostAllowed;
}
/**
* Indicates the desired horizontal accuracy (latitude and longitude).
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* More accurate location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
* {@hide}
*/
public void setHorizontalAccuracy(int accuracy) {
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
throw new IllegalArgumentException("accuracy=" + accuracy);
}
mHorizontalAccuracy = accuracy;
}
/**
* Returns a constant indicating the desired horizontal accuracy (latitude and longitude).
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* {@hide}
*/
public int getHorizontalAccuracy() {
return mHorizontalAccuracy;
}
/**
* Indicates the desired vertical accuracy (altitude).
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* More accurate location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
* {@hide}
*/
public void setVerticalAccuracy(int accuracy) {
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
throw new IllegalArgumentException("accuracy=" + accuracy);
}
mVerticalAccuracy = accuracy;
}
/**
* Returns a constant indicating the desired vertical accuracy (altitude).
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* {@hide}
*/
public int getVerticalAccuracy() {
return mVerticalAccuracy;
}
/**
* Indicates the desired speed accuracy.
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* More accurate location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
* {@hide}
*/
public void setSpeedAccuracy(int accuracy) {
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
throw new IllegalArgumentException("accuracy=" + accuracy);
}
mSpeedAccuracy = accuracy;
}
/**
* Returns a constant indicating the desired speed accuracy
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* {@hide}
*/
public int getSpeedAccuracy() {
return mSpeedAccuracy;
}
/**
* Indicates the desired bearing accuracy.
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* More accurate location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
* {@hide}
*/
public void setBearingAccuracy(int accuracy) {
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
throw new IllegalArgumentException("accuracy=" + accuracy);
}
mBearingAccuracy = accuracy;
}
/**
* Returns a constant indicating the desired bearing accuracy.
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
* {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
* {@hide}
*/
public int getBearingAccuracy() {
return mBearingAccuracy;
}
/**
* Indicates the top priority to optimize for if the criteria parameters are
* found to be in conflict.
* Since a location provider might only be able to optimize for one requirement,
* the other requirements are considered good to have, but not guaranteed.
* This parameter does not override the priorities communicated through the
* preferred accuracy and power consumption parameters.
* If this parameter is not specified and conflicts occur, the location manager
* will use thefollowing default priority (high priority to low priority):
* {@link #HORIZONTAL_ACCURACY_PRIORITY}, {@link #POWER_REQUIREMENT_PRIORITY},
* {@link #VERTICAL_ACCURACY_PRIORITY}, {@link #SPEED_ACCURACY_PRIORITY},
* {@link #BEARING_ACCURACY_PRIORITY}.
* {@hide}
*/
public void setPreferredPriority(int priority) {
if (priority < HORIZONTAL_ACCURACY_PRIORITY || priority > POWER_REQUIREMENT_PRIORITY) {
throw new IllegalArgumentException("priority=" + priority);
}
mPriority = priority;
}
/**
* Returns a constant indicating the top priority to optimize for if the
* criteria parameters are found to be in conflict.
* The value can be {@link #HORIZONTAL_ACCURACY_PRIORITY},
* {@link #VERTICAL_ACCURACY_PRIORITY}, {@link #SPEED_ACCURACY_PRIORITY},
* {@link #BEARING_ACCURACY_PRIORITY} or {@link #POWER_REQUIREMENT_PRIORITY}.
* {@hide}
*/
public int getPriority() {
return mPriority;
}
/**
* Indicates the desired accuracy for latitude and longitude. Accuracy
* may be {@link #ACCURACY_FINE} if desired location
* is fine, else it can be {@link #ACCURACY_COARSE}.
* More accurate location usually consumes more power and may take
* longer.
* More accurate location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is negative
* @throws IllegalArgumentException if accuracy is not one of the supported constants
*/
public void setAccuracy(int accuracy) {
if (accuracy < NO_REQUIREMENT && accuracy > ACCURACY_COARSE) {
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_COARSE) {
throw new IllegalArgumentException("accuracy=" + accuracy);
}
mAccuracy = accuracy;
if (accuracy == ACCURACY_FINE) {
mHorizontalAccuracy = ACCURACY_BEST;
} else {
mHorizontalAccuracy = ACCURACY_LOW;
}
}
/**
@ -108,7 +323,11 @@ public class Criteria implements Parcelable {
* is fine, else it can be {@link #ACCURACY_COARSE}.
*/
public int getAccuracy() {
return mAccuracy;
if (mHorizontalAccuracy >= ACCURACY_HIGH) {
return ACCURACY_FINE;
} else {
return ACCURACY_COARSE;
}
}
/**
@ -131,20 +350,6 @@ public class Criteria implements Parcelable {
return mPowerRequirement;
}
// /**
// * Indicates the preferred response time of the provider, in milliseconds.
// */
// public void setPreferredResponseTime(int time) {
// mPreferredResponseTime = time;
// }
//
// /**
// * Returns the preferred response time of the provider, in milliseconds.
// */
// public int getPreferredResponseTime() {
// return mPreferredResponseTime;
// }
/**
* Indicates whether the provider is allowed to incur monetary cost.
*/
@ -211,9 +416,12 @@ public class Criteria implements Parcelable {
new Parcelable.Creator<Criteria>() {
public Criteria createFromParcel(Parcel in) {
Criteria c = new Criteria();
c.mAccuracy = in.readInt();
c.mHorizontalAccuracy = in.readInt();
c.mVerticalAccuracy = in.readInt();
c.mSpeedAccuracy = in.readInt();
c.mBearingAccuracy = in.readInt();
c.mPriority = in.readInt();
c.mPowerRequirement = in.readInt();
// c.mPreferredResponseTime = in.readInt();
c.mAltitudeRequired = in.readInt() != 0;
c.mBearingRequired = in.readInt() != 0;
c.mSpeedRequired = in.readInt() != 0;
@ -231,9 +439,12 @@ public class Criteria implements Parcelable {
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mAccuracy);
parcel.writeInt(mHorizontalAccuracy);
parcel.writeInt(mVerticalAccuracy);
parcel.writeInt(mSpeedAccuracy);
parcel.writeInt(mBearingAccuracy);
parcel.writeInt(mPriority);
parcel.writeInt(mPowerRequirement);
// parcel.writeInt(mPreferredResponseTime);
parcel.writeInt(mAltitudeRequired ? 1 : 0);
parcel.writeInt(mBearingRequired ? 1 : 0);
parcel.writeInt(mSpeedRequired ? 1 : 0);

View File

@ -18,6 +18,7 @@ package android.location;
import android.app.PendingIntent;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
import android.location.IGpsStatusListener;
@ -32,13 +33,15 @@ import android.os.Bundle;
*/
interface ILocationManager
{
List getAllProviders();
List getProviders(boolean enabledOnly);
List<String> getAllProviders();
List<String> getProviders(in Criteria criteria, boolean enabledOnly);
String getBestProvider(in Criteria criteria, boolean enabledOnly);
boolean providerMeetsCriteria(String provider, in Criteria criteria);
void requestLocationUpdates(String provider, long minTime, float minDistance,
in ILocationListener listener);
void requestLocationUpdatesPI(String provider, long minTime, float minDistance,
in PendingIntent intent);
void requestLocationUpdates(String provider, in Criteria criteria, long minTime, float minDistance,
boolean singleShot, in ILocationListener listener);
void requestLocationUpdatesPI(String provider, in Criteria criteria, long minTime, float minDistance,
boolean singleShot, in PendingIntent intent);
void removeUpdates(in ILocationListener listener);
void removeUpdatesPI(in PendingIntent intent);

View File

@ -16,6 +16,7 @@
package android.location;
import android.location.Criteria;
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
@ -34,6 +35,7 @@ interface ILocationProvider {
boolean supportsSpeed();
boolean supportsBearing();
int getPowerRequirement();
boolean meetsCriteria(in Criteria criteria);
int getAccuracy();
void enable();
void disable();

View File

@ -28,8 +28,6 @@ import android.util.Log;
import com.android.internal.location.DummyLocationProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@ -249,15 +247,12 @@ public class LocationManager {
* factory Context.getSystemService.
*/
public LocationManager(ILocationManager service) {
if (false) {
Log.d(TAG, "Constructor: service = " + service);
}
mService = service;
}
private LocationProvider createProvider(String name, Bundle info) {
DummyLocationProvider provider =
new DummyLocationProvider(name);
new DummyLocationProvider(name, mService);
provider.setRequiresNetwork(info.getBoolean("network"));
provider.setRequiresSatellite(info.getBoolean("satellite"));
provider.setRequiresCell(info.getBoolean("cell"));
@ -299,7 +294,7 @@ public class LocationManager {
*/
public List<String> getProviders(boolean enabledOnly) {
try {
return mService.getProviders(enabledOnly);
return mService.getProviders(null, enabledOnly);
} catch (RemoteException ex) {
Log.e(TAG, "getProviders: RemoteException", ex);
}
@ -344,173 +339,15 @@ public class LocationManager {
* @return list of Strings containing names of the providers
*/
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
List<String> goodProviders = Collections.emptyList();
List<String> providers = getProviders(enabledOnly);
for (String providerName : providers) {
LocationProvider provider = getProvider(providerName);
if (provider != null && provider.meetsCriteria(criteria)) {
if (goodProviders.isEmpty()) {
goodProviders = new ArrayList<String>();
}
goodProviders.add(providerName);
}
if (criteria == null) {
throw new IllegalArgumentException("criteria==null");
}
return goodProviders;
}
/**
* Returns the next looser power requirement, in the sequence:
*
* POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
*/
private int nextPower(int power) {
switch (power) {
case Criteria.POWER_LOW:
return Criteria.POWER_MEDIUM;
case Criteria.POWER_MEDIUM:
return Criteria.POWER_HIGH;
case Criteria.POWER_HIGH:
return Criteria.NO_REQUIREMENT;
case Criteria.NO_REQUIREMENT:
default:
return Criteria.NO_REQUIREMENT;
try {
return mService.getProviders(criteria, enabledOnly);
} catch (RemoteException ex) {
Log.e(TAG, "getProviders: RemoteException", ex);
}
}
/**
* Returns the next looser accuracy requirement, in the sequence:
*
* ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
*/
private int nextAccuracy(int accuracy) {
if (accuracy == Criteria.ACCURACY_FINE) {
return Criteria.ACCURACY_COARSE;
} else {
return Criteria.NO_REQUIREMENT;
}
}
private abstract class LpComparator implements Comparator<LocationProvider> {
public int compare(int a1, int a2) {
if (a1 < a2) {
return -1;
} else if (a1 > a2) {
return 1;
} else {
return 0;
}
}
public int compare(float a1, float a2) {
if (a1 < a2) {
return -1;
} else if (a1 > a2) {
return 1;
} else {
return 0;
}
}
}
private class LpPowerComparator extends LpComparator {
public int compare(LocationProvider l1, LocationProvider l2) {
int a1 = l1.getPowerRequirement();
int a2 = l2.getPowerRequirement();
return compare(a1, a2); // Smaller is better
}
public boolean equals(LocationProvider l1, LocationProvider l2) {
int a1 = l1.getPowerRequirement();
int a2 = l2.getPowerRequirement();
return a1 == a2;
}
}
private class LpAccuracyComparator extends LpComparator {
public int compare(LocationProvider l1, LocationProvider l2) {
int a1 = l1.getAccuracy();
int a2 = l2.getAccuracy();
return compare(a1, a2); // Smaller is better
}
public boolean equals(LocationProvider l1, LocationProvider l2) {
int a1 = l1.getAccuracy();
int a2 = l2.getAccuracy();
return a1 == a2;
}
}
private class LpCapabilityComparator extends LpComparator {
private static final int ALTITUDE_SCORE = 4;
private static final int BEARING_SCORE = 4;
private static final int SPEED_SCORE = 4;
private int score(LocationProvider p) {
return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
(p.supportsBearing() ? BEARING_SCORE : 0) +
(p.supportsSpeed() ? SPEED_SCORE : 0);
}
public int compare(LocationProvider l1, LocationProvider l2) {
int a1 = score(l1);
int a2 = score(l2);
return compare(-a1, -a2); // Bigger is better
}
public boolean equals(LocationProvider l1, LocationProvider l2) {
int a1 = score(l1);
int a2 = score(l2);
return a1 == a2;
}
}
private LocationProvider best(List<String> providerNames) {
List<LocationProvider> providers = new ArrayList<LocationProvider>(providerNames.size());
for (String name : providerNames) {
providers.add(getProvider(name));
}
if (providers.size() < 2) {
return providers.get(0);
}
// First, sort by power requirement
Collections.sort(providers, new LpPowerComparator());
int power = providers.get(0).getPowerRequirement();
if (power < providers.get(1).getPowerRequirement()) {
return providers.get(0);
}
int idx, size;
List<LocationProvider> tmp = new ArrayList<LocationProvider>();
idx = 0;
size = providers.size();
while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
tmp.add(providers.get(idx));
idx++;
}
// Next, sort by accuracy
Collections.sort(tmp, new LpAccuracyComparator());
int acc = tmp.get(0).getAccuracy();
if (acc < tmp.get(1).getAccuracy()) {
return tmp.get(0);
}
List<LocationProvider> tmp2 = new ArrayList<LocationProvider>();
idx = 0;
size = tmp.size();
while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
tmp2.add(tmp.get(idx));
idx++;
}
// Finally, sort by capability "score"
Collections.sort(tmp2, new LpCapabilityComparator());
return tmp2.get(0);
return null;
}
/**
@ -536,72 +373,14 @@ public class LocationManager {
* @return name of the provider that best matches the requirements
*/
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
List<String> goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
if (criteria == null) {
throw new IllegalArgumentException("criteria==null");
}
// Make a copy of the criteria that we can modify
criteria = new Criteria(criteria);
// Loosen power requirement
int power = criteria.getPowerRequirement();
while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
power = nextPower(power);
criteria.setPowerRequirement(power);
goodProviders = getProviders(criteria, enabledOnly);
try {
return mService.getBestProvider(criteria, enabledOnly);
} catch (RemoteException ex) {
Log.e(TAG, "getBestProvider: RemoteException", ex);
}
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// // Loosen response time requirement
// int responseTime = criteria.getPreferredResponseTime();
// while (goodProviders.isEmpty() &&
// (responseTime != Criteria.NO_REQUIREMENT)) {
// responseTime += 1000;
// if (responseTime > 60000) {
// responseTime = Criteria.NO_REQUIREMENT;
// }
// criteria.setPreferredResponseTime(responseTime);
// goodProviders = getProviders(criteria);
// }
// if (!goodProviders.isEmpty()) {
// return best(goodProviders);
// }
// Loosen accuracy requirement
int accuracy = criteria.getAccuracy();
while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
accuracy = nextAccuracy(accuracy);
criteria.setAccuracy(accuracy);
goodProviders = getProviders(criteria, enabledOnly);
}
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Remove bearing requirement
criteria.setBearingRequired(false);
goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Remove speed requirement
criteria.setSpeedRequired(false);
goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Remove altitude requirement
criteria.setAltitudeRequired(false);
goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
return null;
}
@ -658,7 +437,7 @@ public class LocationManager {
if (listener == null) {
throw new IllegalArgumentException("listener==null");
}
_requestLocationUpdates(provider, minTime, minDistance, listener, null);
_requestLocationUpdates(provider, null, minTime, minDistance, false, listener, null);
}
/**
@ -701,10 +480,10 @@ public class LocationManager {
* each location update
* @param looper a Looper object whose message queue will be used to
* implement the callback mechanism.
* If looper is null then the callbacks will be called on the main thread.
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
* @throws IllegalArgumentException if looper is null
* @throws SecurityException if no suitable permission is present for the provider.
*/
public void requestLocationUpdates(String provider,
@ -716,15 +495,72 @@ public class LocationManager {
if (listener == null) {
throw new IllegalArgumentException("listener==null");
}
if (looper == null) {
throw new IllegalArgumentException("looper==null");
}
_requestLocationUpdates(provider, minTime, minDistance, listener, looper);
_requestLocationUpdates(provider, null, minTime, minDistance, false, listener, looper);
}
private void _requestLocationUpdates(String provider,
long minTime, float minDistance, LocationListener listener,
Looper looper) {
/**
* Registers the current activity to be notified periodically based on
* the specified criteria. Periodically, the supplied LocationListener will
* be called with the current Location or with status updates.
*
* <p> It may take a while to receive the most recent location. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
* <p> In case the provider is disabled by the user, updates will stop,
* and the {@link LocationListener#onProviderDisabled(String)}
* method will be called. As soon as the provider is enabled again,
* the {@link LocationListener#onProviderEnabled(String)} method will
* be called and location updates will start again.
*
* <p> The frequency of notification may be controlled using the
* minTime and minDistance parameters. If minTime is greater than 0,
* the LocationManager could potentially rest for minTime milliseconds
* between location updates to conserve power. If minDistance is greater than 0,
* a location will only be broadcasted if the device moves by minDistance meters.
* To obtain notifications as frequently as possible, set both parameters to 0.
*
* <p> Background services should be careful about setting a sufficiently high
* minTime so that the device doesn't consume too much power by keeping the
* GPS or wireless radios on all the time. In particular, values under 60000ms
* are not recommended.
*
* <p> The supplied Looper is used to implement the callback mechanism.
*
* @param minTime the minimum time interval for notifications, in
* milliseconds. This field is only used as a hint to conserve power, and actual
* time between location updates may be greater or lesser than this value.
* @param minDistance the minimum distance interval for notifications,
* in meters
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
* @param listener a {#link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @param looper a Looper object whose message queue will be used to
* implement the callback mechanism.
* If looper is null then the callbacks will be called on the main thread.
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present to access
* the location services.
*
* {@hide}
*/
public void requestLocationUpdates(long minTime, float minDistance,
Criteria criteria, LocationListener listener, Looper looper) {
if (criteria == null) {
throw new IllegalArgumentException("criteria==null");
}
if (listener == null) {
throw new IllegalArgumentException("listener==null");
}
_requestLocationUpdates(null, criteria, minTime, minDistance, false, listener, looper);
}
private void _requestLocationUpdates(String provider, Criteria criteria, long minTime,
float minDistance, boolean singleShot, LocationListener listener, Looper looper) {
if (minTime < 0L) {
minTime = 0L;
}
@ -739,7 +575,7 @@ public class LocationManager {
transport = new ListenerTransport(listener, looper);
}
mListeners.put(listener, transport);
mService.requestLocationUpdates(provider, minTime, minDistance, transport);
mService.requestLocationUpdates(provider, criteria, minTime, minDistance, singleShot, transport);
}
} catch (RemoteException ex) {
Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
@ -785,7 +621,7 @@ public class LocationManager {
* time between location updates may be greater or lesser than this value.
* @param minDistance the minimum distance interval for notifications,
* in meters
* @param intent a {#link PendingIntet} to be sent for each location update
* @param intent a {#link PendingIntent} to be sent for each location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
@ -799,11 +635,69 @@ public class LocationManager {
if (intent == null) {
throw new IllegalArgumentException("intent==null");
}
_requestLocationUpdates(provider, minTime, minDistance, intent);
_requestLocationUpdates(provider, null, minTime, minDistance, false, intent);
}
private void _requestLocationUpdates(String provider,
long minTime, float minDistance, PendingIntent intent) {
/**
* Registers the current activity to be notified periodically based on
* the specified criteria. Periodically, the supplied PendingIntent will
* be broadcast with the current Location or with status updates.
*
* <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
*
* <p> It may take a while to receive the most recent location. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
* <p> The frequency of notification or new locations may be controlled using the
* minTime and minDistance parameters. If minTime is greater than 0,
* the LocationManager could potentially rest for minTime milliseconds
* between location updates to conserve power. If minDistance is greater than 0,
* a location will only be broadcast if the device moves by minDistance meters.
* To obtain notifications as frequently as possible, set both parameters to 0.
*
* <p> Background services should be careful about setting a sufficiently high
* minTime so that the device doesn't consume too much power by keeping the
* GPS or wireless radios on all the time. In particular, values under 60000ms
* are not recommended.
*
* <p> In case the provider is disabled by the user, updates will stop,
* and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
* of false. If the provider is re-enabled, an intent will be sent with an
* extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
* start again.
*
* <p> If the provider's status changes, an intent will be sent with an extra with key
* KEY_STATUS_CHANGED and an integer value indicating the new status. Any extras associated
* with the status update will be sent as well.
*
* @param minTime the minimum time interval for notifications, in
* milliseconds. This field is only used as a hint to conserve power, and actual
* time between location updates may be greater or lesser than this value.
* @param minDistance the minimum distance interval for notifications,
* in meters
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
* @param intent a {#link PendingIntent} to be sent for each location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present for the provider.
*
* {@hide}
*/
public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent) {
if (criteria == null) {
throw new IllegalArgumentException("criteria==null");
}
if (intent == null) {
throw new IllegalArgumentException("intent==null");
}
_requestLocationUpdates(null, criteria, minTime, minDistance, false, intent);
}
private void _requestLocationUpdates(String provider, Criteria criteria,
long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
if (minTime < 0L) {
minTime = 0L;
}
@ -812,12 +706,157 @@ public class LocationManager {
}
try {
mService.requestLocationUpdatesPI(provider, minTime, minDistance, intent);
mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, intent);
} catch (RemoteException ex) {
Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
}
}
/**
* Registers the current activity to be notified periodically by
* the named provider. Periodically, the supplied LocationListener will
* be called with the current Location or with status updates.
*
* <p> It may take a while to receive the most recent location. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
* <p> In case the provider is disabled by the user, updates will stop,
* and the {@link LocationListener#onProviderDisabled(String)}
* method will be called. As soon as the provider is enabled again,
* the {@link LocationListener#onProviderEnabled(String)} method will
* be called and location updates will start again.
*
* <p> The supplied Looper is used to implement the callback mechanism.
*
* @param provider the name of the provider with which to register
* @param listener a {#link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @param looper a Looper object whose message queue will be used to
* implement the callback mechanism.
* If looper is null then the callbacks will be called on the main thread.
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present for the provider.
*
* {@hide}
*/
public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
if (provider == null) {
throw new IllegalArgumentException("provider==null");
}
if (listener == null) {
throw new IllegalArgumentException("listener==null");
}
_requestLocationUpdates(provider, null, 0L, 0.0f, true, listener, looper);
}
/**
* Registers the current activity to be notified periodically based on
* the specified criteria. Periodically, the supplied LocationListener will
* be called with the current Location or with status updates.
*
* <p> It may take a while to receive the most recent location. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
* <p> In case the provider is disabled by the user, updates will stop,
* and the {@link LocationListener#onProviderDisabled(String)}
* method will be called. As soon as the provider is enabled again,
* the {@link LocationListener#onProviderEnabled(String)} method will
* be called and location updates will start again.
*
* <p> The supplied Looper is used to implement the callback mechanism.
*
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
* @param listener a {#link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @param looper a Looper object whose message queue will be used to
* implement the callback mechanism.
* If looper is null then the callbacks will be called on the current thread.
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present to access
* the location services.
*
* {@hide}
*/
public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
if (criteria == null) {
throw new IllegalArgumentException("criteria==null");
}
if (listener == null) {
throw new IllegalArgumentException("listener==null");
}
_requestLocationUpdates(null, criteria, 0L, 0.0f, true, listener, looper);
}
/**
* Registers the current activity to be notified periodically by
* the named provider. Periodically, the supplied PendingIntent will
* be broadcast with the current Location or with status updates.
*
* <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
*
* <p> It may take a while to receive the most recent location. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
* @param provider the name of the provider with which to register
* @param intent a {#link PendingIntent} to be sent for the location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present for the provider.
*
* {@hide}
*/
public void requestSingleUpdate(String provider, PendingIntent intent) {
if (provider == null) {
throw new IllegalArgumentException("provider==null");
}
if (intent == null) {
throw new IllegalArgumentException("intent==null");
}
_requestLocationUpdates(provider, null, 0L, 0.0f, true, intent);
}
/**
* Registers the current activity to be notified periodically based on
* the specified criteria. Periodically, the supplied PendingIntent will
* be broadcast with the current Location or with status updates.
*
* <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
*
* <p> It may take a while to receive the most recent location. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
* @param intent a {#link PendingIntent} to be sent for the location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present for the provider.
*
* {@hide}
*/
public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
if (criteria == null) {
throw new IllegalArgumentException("criteria==null");
}
if (intent == null) {
throw new IllegalArgumentException("intent==null");
}
_requestLocationUpdates(null, criteria, 0L, 0.0f, true, intent);
}
/**
* Removes any current registration for location updates of the current activity
* with the given LocationListener. Following this call, updates will no longer

View File

@ -16,6 +16,9 @@
package android.location;
import android.os.RemoteException;
import android.util.Log;
/**
* An abstract superclass for location providers. A location provider
* provides periodic reports on the geographical location of the
@ -36,7 +39,8 @@ public abstract class LocationProvider {
// in the name of a LocationProvider.
static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
private String mName;
private final String mName;
private final ILocationManager mService;
public static final int OUT_OF_SERVICE = 0;
public static final int TEMPORARILY_UNAVAILABLE = 1;
@ -50,13 +54,13 @@ public abstract class LocationProvider {
*
* {@hide}
*/
public LocationProvider(String name) {
public LocationProvider(String name, ILocationManager service) {
if (name.matches(BAD_CHARS_REGEX)) {
throw new IllegalArgumentException("name " + name +
" contains an illegal character");
}
// Log.d(TAG, "Constructor: name = " + name);
mName = name;
mService = service;
}
/**
@ -71,29 +75,12 @@ public abstract class LocationProvider {
* false otherwise.
*/
public boolean meetsCriteria(Criteria criteria) {
// We do not want to match the special passive provider based on criteria.
if (LocationManager.PASSIVE_PROVIDER.equals(mName)) {
try {
return mService.providerMeetsCriteria(mName, criteria);
} catch (RemoteException e) {
Log.e(TAG, "meetsCriteria: RemoteException", e);
return false;
}
if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
(criteria.getAccuracy() < getAccuracy())) {
return false;
}
int criteriaPower = criteria.getPowerRequirement();
if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
(criteriaPower < getPowerRequirement())) {
return false;
}
if (criteria.isAltitudeRequired() && !supportsAltitude()) {
return false;
}
if (criteria.isSpeedRequired() && !supportsSpeed()) {
return false;
}
if (criteria.isBearingRequired() && !supportsBearing()) {
return false;
}
return true;
}
/**

View File

@ -18,6 +18,7 @@ package android.location.provider;
import android.content.Context;
import android.net.NetworkInfo;
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.ILocationProvider;
import android.location.Location;
@ -75,6 +76,10 @@ public abstract class LocationProvider {
return LocationProvider.this.onGetPowerRequirement();
}
public boolean meetsCriteria(Criteria criteria) {
return LocationProvider.this.onMeetsCriteria(criteria);
}
public int getAccuracy() {
return LocationProvider.this.onGetAccuracy();
}
@ -225,6 +230,12 @@ public abstract class LocationProvider {
*/
public abstract int onGetPowerRequirement();
/**
* Returns true if this provider meets the given criteria,
* false otherwise.
*/
public abstract boolean onMeetsCriteria(Criteria criteria);
/**
* Returns a constant describing horizontal accuracy of this provider.
* If the provider returns finer grain or exact location,

View File

@ -16,6 +16,7 @@
package com.android.internal.location;
import android.location.ILocationManager;
import android.location.LocationProvider;
/**
@ -41,8 +42,8 @@ public class DummyLocationProvider extends LocationProvider {
int mPowerRequirement;
int mAccuracy;
public DummyLocationProvider(String name) {
super(name);
public DummyLocationProvider(String name, ILocationManager service) {
super(name, service);
}
public void setRequiresNetwork(boolean requiresNetwork) {

View File

@ -30,6 +30,7 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
@ -68,6 +69,8 @@ import com.android.server.location.PassiveProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -610,10 +613,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return out;
}
public List<String> getProviders(boolean enabledOnly) {
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
try {
synchronized (mLock) {
return _getProvidersLocked(enabledOnly);
return _getProvidersLocked(criteria, enabledOnly);
}
} catch (SecurityException se) {
throw se;
@ -623,7 +626,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
private List<String> _getProvidersLocked(boolean enabledOnly) {
private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) {
if (LOCAL_LOGV) {
Slog.v(TAG, "getProviders");
}
@ -635,12 +638,225 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (enabledOnly && !isAllowedBySettingsLocked(name)) {
continue;
}
if (criteria != null && !p.meetsCriteria(criteria)) {
continue;
}
out.add(name);
}
}
return out;
}
/**
* Returns the next looser power requirement, in the sequence:
*
* POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
*/
private int nextPower(int power) {
switch (power) {
case Criteria.POWER_LOW:
return Criteria.POWER_MEDIUM;
case Criteria.POWER_MEDIUM:
return Criteria.POWER_HIGH;
case Criteria.POWER_HIGH:
return Criteria.NO_REQUIREMENT;
case Criteria.NO_REQUIREMENT:
default:
return Criteria.NO_REQUIREMENT;
}
}
/**
* Returns the next looser accuracy requirement, in the sequence:
*
* ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
*/
private int nextAccuracy(int accuracy) {
if (accuracy == Criteria.ACCURACY_FINE) {
return Criteria.ACCURACY_COARSE;
} else {
return Criteria.NO_REQUIREMENT;
}
}
private class LpPowerComparator implements Comparator<LocationProviderInterface> {
public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
// Smaller is better
return (l1.getPowerRequirement() - l2.getPowerRequirement());
}
public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
return (l1.getPowerRequirement() == l2.getPowerRequirement());
}
}
private class LpAccuracyComparator implements Comparator<LocationProviderInterface> {
public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
// Smaller is better
return (l1.getAccuracy() - l2.getAccuracy());
}
public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
return (l1.getAccuracy() == l2.getAccuracy());
}
}
private class LpCapabilityComparator implements Comparator<LocationProviderInterface> {
private static final int ALTITUDE_SCORE = 4;
private static final int BEARING_SCORE = 4;
private static final int SPEED_SCORE = 4;
private int score(LocationProviderInterface p) {
return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
(p.supportsBearing() ? BEARING_SCORE : 0) +
(p.supportsSpeed() ? SPEED_SCORE : 0);
}
public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
return (score(l2) - score(l1)); // Bigger is better
}
public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
return (score(l1) == score(l2));
}
}
private LocationProviderInterface best(List<String> providerNames) {
ArrayList<LocationProviderInterface> providers;
synchronized (mLock) {
providers = new ArrayList<LocationProviderInterface>(mProviders.size());
for (int i = mProviders.size() - 1; i >= 0; i--) {
providers.add(mProviders.get(i));
}
}
if (providers.size() < 2) {
return providers.get(0);
}
// First, sort by power requirement
Collections.sort(providers, new LpPowerComparator());
int power = providers.get(0).getPowerRequirement();
if (power < providers.get(1).getPowerRequirement()) {
return providers.get(0);
}
int idx, size;
ArrayList<LocationProviderInterface> tmp = new ArrayList<LocationProviderInterface>();
idx = 0;
size = providers.size();
while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
tmp.add(providers.get(idx));
idx++;
}
// Next, sort by accuracy
Collections.sort(tmp, new LpAccuracyComparator());
int acc = tmp.get(0).getAccuracy();
if (acc < tmp.get(1).getAccuracy()) {
return tmp.get(0);
}
ArrayList<LocationProviderInterface> tmp2 = new ArrayList<LocationProviderInterface>();
idx = 0;
size = tmp.size();
while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
tmp2.add(tmp.get(idx));
idx++;
}
// Finally, sort by capability "score"
Collections.sort(tmp2, new LpCapabilityComparator());
return tmp2.get(0);
}
/**
* Returns the name of the provider that best meets the given criteria. Only providers
* that are permitted to be accessed by the calling activity will be
* returned. If several providers meet the criteria, the one with the best
* accuracy is returned. If no provider meets the criteria,
* the criteria are loosened in the following sequence:
*
* <ul>
* <li> power requirement
* <li> accuracy
* <li> bearing
* <li> speed
* <li> altitude
* </ul>
*
* <p> Note that the requirement on monetary cost is not removed
* in this process.
*
* @param criteria the criteria that need to be matched
* @param enabledOnly if true then only a provider that is currently enabled is returned
* @return name of the provider that best matches the requirements
*/
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
List<String> goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Make a copy of the criteria that we can modify
criteria = new Criteria(criteria);
// Loosen power requirement
int power = criteria.getPowerRequirement();
while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
power = nextPower(power);
criteria.setPowerRequirement(power);
goodProviders = getProviders(criteria, enabledOnly);
}
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Loosen accuracy requirement
int accuracy = criteria.getAccuracy();
while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
accuracy = nextAccuracy(accuracy);
criteria.setAccuracy(accuracy);
goodProviders = getProviders(criteria, enabledOnly);
}
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Remove bearing requirement
criteria.setBearingRequired(false);
goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Remove speed requirement
criteria.setSpeedRequired(false);
goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
// Remove altitude requirement
criteria.setAltitudeRequired(false);
goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
return best(goodProviders).getName();
}
return null;
}
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
}
return p.meetsCriteria(criteria);
}
private void updateProvidersLocked() {
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface p = mProviders.get(i);
@ -717,6 +933,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
final Receiver mReceiver;
final long mMinTime;
final float mMinDistance;
final boolean mSingleShot;
final int mUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
@ -724,12 +941,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
/**
* Note: must be constructed with lock held.
*/
UpdateRecord(String provider, long minTime, float minDistance,
UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot,
Receiver receiver, int uid) {
mProvider = provider;
mReceiver = receiver;
mMinTime = minTime;
mMinDistance = minDistance;
mSingleShot = singleShot;
mUid = uid;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@ -764,6 +982,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
pw.println(prefix + this);
pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
pw.println(prefix + "mSingleShot=" + mSingleShot);
pw.println(prefix + "mUid=" + mUid);
pw.println(prefix + "mLastFixBroadcast:");
if (mLastFixBroadcast != null) {
@ -819,12 +1038,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return false;
}
public void requestLocationUpdates(String provider,
long minTime, float minDistance, ILocationListener listener) {
public void requestLocationUpdates(String provider, Criteria criteria,
long minTime, float minDistance, boolean singleShot, ILocationListener listener) {
if (criteria != null) {
// FIXME - should we consider using multiple providers simultaneously
// rather than only the best one?
// Should we do anything different for single shot fixes?
provider = getBestProvider(criteria, true);
if (provider == null) {
throw new IllegalArgumentException("no providers found for criteria");
}
}
try {
synchronized (mLock) {
requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
getReceiver(listener));
}
} catch (SecurityException se) {
throw se;
@ -835,11 +1063,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
public void requestLocationUpdatesPI(String provider,
long minTime, float minDistance, PendingIntent intent) {
public void requestLocationUpdatesPI(String provider, Criteria criteria,
long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
if (criteria != null) {
// FIXME - should we consider using multiple providers simultaneously
// rather than only the best one?
// Should we do anything different for single shot fixes?
provider = getBestProvider(criteria, true);
if (provider == null) {
throw new IllegalArgumentException("no providers found for criteria");
}
}
try {
synchronized (mLock) {
requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
getReceiver(intent));
}
} catch (SecurityException se) {
throw se;
@ -850,8 +1088,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
private void requestLocationUpdatesLocked(String provider,
long minTime, float minDistance, Receiver receiver) {
private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance,
boolean singleShot, Receiver receiver) {
if (LOCAL_LOGV) {
Slog.v(TAG, "_requestLocationUpdates: listener = " + receiver);
}
@ -868,7 +1106,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
boolean newUid = !providerHasListener(provider, callingUid, null);
long identity = Binder.clearCallingIdentity();
try {
UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot,
receiver, callingUid);
UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
if (oldRecord != null) {
oldRecord.disposeLocked();
@ -882,7 +1121,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (isProviderEnabled) {
long minTimeForProvider = getMinTimeLocked(provider);
p.setMinTime(minTimeForProvider);
p.enableLocationTracking(true);
// try requesting single shot if singleShot is true, and fall back to
// regular location tracking if requestSingleShotFix() is not supported
if (!singleShot || !p.requestSingleShotFix()) {
p.enableLocationTracking(true);
}
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(provider, false);
@ -1288,7 +1531,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface provider = mProviders.get(i);
requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f,
false, mProximityReceiver);
}
}
}
@ -1486,6 +1730,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (int i=0; i<N; i++) {
UpdateRecord r = records.get(i);
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
@ -1497,10 +1742,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
if (!receiver.callLocationChangedLocked(location)) {
Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
}
deadReceivers.add(receiver);
receiverDead = true;
}
}
@ -1510,13 +1752,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
receiverDead = true;
Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
}
if (!deadReceivers.contains(receiver)) {
deadReceivers.add(receiver);
}
}
}
// remove receiver if it is dead or we just processed a single shot request
if (receiverDead || r.mSingleShot) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
}
if (!deadReceivers.contains(receiver)) {
deadReceivers.add(receiver);
}
}
}

View File

@ -150,6 +150,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final int UPDATE_LOCATION = 7;
private static final int ADD_LISTENER = 8;
private static final int REMOVE_LISTENER = 9;
private static final int REQUEST_SINGLE_SHOT = 10;
private static final String PROPERTIES_FILE = "/etc/gps.conf";
@ -190,6 +191,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
// true if we started navigation
private boolean mStarted;
// true if single shot request is in progress
private boolean mSingleShot;
// capabilities of the GPS engine
private int mEngineCapabilities;
@ -318,7 +322,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (action.equals(ALARM_WAKEUP)) {
if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
startNavigating();
startNavigating(false);
} else if (action.equals(ALARM_TIMEOUT)) {
if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
@ -598,6 +602,14 @@ public class GpsLocationProvider implements LocationProviderInterface {
return Criteria.POWER_HIGH;
}
/**
* Returns true if this provider meets the given criteria,
* false otherwise.
*/
public boolean meetsCriteria(Criteria criteria) {
return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
}
/**
* Returns the horizontal accuracy of this provider
*
@ -707,6 +719,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
public void enableLocationTracking(boolean enable) {
// FIXME - should set a flag here to avoid race conditions with single shot request
synchronized (mHandler) {
sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
}
@ -716,7 +729,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (enable) {
mTTFF = 0;
mLastFixTime = 0;
startNavigating();
startNavigating(false);
} else {
if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
mAlarmManager.cancel(mWakeupIntent);
@ -726,6 +739,25 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
public boolean requestSingleShotFix() {
if (mStarted) {
// cannot do single shot if already navigating
return false;
}
synchronized (mHandler) {
mHandler.removeMessages(REQUEST_SINGLE_SHOT);
Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
mHandler.sendMessage(m);
}
return true;
}
private void handleRequestSingleShot() {
mTTFF = 0;
mLastFixTime = 0;
startNavigating(true);
}
public void setMinTime(long minTime) {
if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
@ -875,16 +907,20 @@ public class GpsLocationProvider implements LocationProviderInterface {
return false;
}
private void startNavigating() {
private void startNavigating(boolean singleShot) {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
mStarted = true;
if (hasCapability(GPS_CAPABILITY_MSB) &&
Settings.Secure.getInt(mContext.getContentResolver(),
mSingleShot = singleShot;
mPositionMode = GPS_POSITION_MODE_STANDALONE;
if (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
mPositionMode = GPS_POSITION_MODE_MS_BASED;
} else {
mPositionMode = GPS_POSITION_MODE_STANDALONE;
if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
} else if (hasCapability(GPS_CAPABILITY_MSA)) {
mPositionMode = GPS_POSITION_MODE_MS_BASED;
}
}
int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
@ -918,6 +954,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
mStarted = false;
mSingleShot = false;
native_stop();
mTTFF = 0;
mLastFixTime = 0;
@ -1008,6 +1045,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
if (mSingleShot) {
stopNavigating();
}
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// we want to time out if we do not receive a fix
// within the time out and we are requesting infrequent fixes
@ -1022,7 +1062,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
updateStatus(LocationProvider.AVAILABLE, mSvCount);
}
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval > 1000) {
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && mFixInterval > 1000) {
if (DEBUG) Log.d(TAG, "got fix, hibernating");
hibernate();
}
@ -1369,6 +1409,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
case ENABLE_TRACKING:
handleEnableLocationTracking(msg.arg1 == 1);
break;
case REQUEST_SINGLE_SHOT:
handleRequestSingleShot();
break;
case UPDATE_NETWORK_STATE:
handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
break;

View File

@ -16,6 +16,7 @@
package com.android.server.location;
import android.location.Criteria;
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
@ -35,6 +36,7 @@ public interface LocationProviderInterface {
boolean supportsSpeed();
boolean supportsBearing();
int getPowerRequirement();
boolean meetsCriteria(Criteria criteria);
int getAccuracy();
boolean isEnabled();
void enable();
@ -42,6 +44,8 @@ public interface LocationProviderInterface {
int getStatus(Bundle extras);
long getStatusUpdateTime();
void enableLocationTracking(boolean enable);
/* returns false if single shot is not supported */
boolean requestSingleShotFix();
String getInternalState();
void setMinTime(long minTime);
void updateNetworkState(int state, NetworkInfo info);

View File

@ -20,6 +20,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Criteria;
import android.location.ILocationProvider;
import android.location.Location;
import android.net.NetworkInfo;
@ -97,7 +98,7 @@ public class LocationProviderProxy implements LocationProviderInterface {
if (mCachedAttributes == null) {
try {
mCachedAttributes = new DummyLocationProvider(mName);
mCachedAttributes = new DummyLocationProvider(mName, null);
mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
mCachedAttributes.setRequiresCell(provider.requiresCell());
@ -199,6 +200,39 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
}
public boolean meetsCriteria(Criteria criteria) {
ILocationProvider provider;
synchronized (mServiceConnection) {
provider = mProvider;
}
if (provider != null) {
try {
return provider.meetsCriteria(criteria);
} catch (RemoteException e) {
}
}
// default implementation if we lost connection to the provider
if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
(criteria.getAccuracy() < getAccuracy())) {
return false;
}
int criteriaPower = criteria.getPowerRequirement();
if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
(criteriaPower < getPowerRequirement())) {
return false;
}
if (criteria.isAltitudeRequired() && !supportsAltitude()) {
return false;
}
if (criteria.isSpeedRequired() && !supportsSpeed()) {
return false;
}
if (criteria.isBearingRequired() && !supportsBearing()) {
return false;
}
return true;
}
public int getAccuracy() {
if (mCachedAttributes != null) {
return mCachedAttributes.getAccuracy();
@ -297,6 +331,10 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
}
public boolean requestSingleShotFix() {
return false;
}
public long getMinTime() {
return mMinTime;
}

View File

@ -16,6 +16,7 @@
package com.android.server.location;
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
@ -138,6 +139,28 @@ public class MockProvider implements LocationProviderInterface {
return mSupportsSpeed;
}
public boolean meetsCriteria(Criteria criteria) {
if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
(criteria.getAccuracy() < mAccuracy)) {
return false;
}
int criteriaPower = criteria.getPowerRequirement();
if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
(criteriaPower < mPowerRequirement)) {
return false;
}
if (criteria.isAltitudeRequired() && !mSupportsAltitude) {
return false;
}
if (criteria.isSpeedRequired() && !mSupportsSpeed) {
return false;
}
if (criteria.isBearingRequired() && !mSupportsBearing) {
return false;
}
return true;
}
public void setLocation(Location l) {
mLocation.set(l);
mHasLocation = true;
@ -174,6 +197,10 @@ public class MockProvider implements LocationProviderInterface {
public void enableLocationTracking(boolean enable) {
}
public boolean requestSingleShotFix() {
return false;
}
public void setMinTime(long minTime) {
}

View File

@ -16,6 +16,7 @@
package com.android.server.location;
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
@ -79,6 +80,11 @@ public class PassiveProvider implements LocationProviderInterface {
return -1;
}
public boolean meetsCriteria(Criteria criteria) {
// We do not want to match the special passive provider based on criteria.
return false;
}
public int getAccuracy() {
return -1;
}
@ -113,6 +119,10 @@ public class PassiveProvider implements LocationProviderInterface {
mTracking = enable;
}
public boolean requestSingleShotFix() {
return false;
}
public void setMinTime(long minTime) {
}