summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk6
-rw-r--r--api/current.txt128
-rwxr-xr-xcore/res/res/values/config.xml27
-rw-r--r--core/res/res/values/public.xml4
-rw-r--r--location/java/android/location/Criteria.java45
-rw-r--r--location/java/android/location/Geofence.aidl19
-rw-r--r--location/java/android/location/Geofence.java174
-rw-r--r--location/java/android/location/ILocationManager.aidl72
-rw-r--r--location/java/android/location/Location.java68
-rw-r--r--location/java/android/location/LocationManager.java606
-rw-r--r--location/java/android/location/LocationProvider.java114
-rw-r--r--location/java/android/location/LocationRequest.aidl19
-rw-r--r--location/java/android/location/LocationRequest.java321
-rw-r--r--location/java/com/android/internal/location/DummyLocationProvider.java180
-rw-r--r--location/java/com/android/internal/location/ILocationProvider.aidl (renamed from location/java/android/location/ILocationProvider.aidl)33
-rw-r--r--location/java/com/android/internal/location/ProviderProperties.aidl19
-rw-r--r--location/java/com/android/internal/location/ProviderProperties.java152
-rw-r--r--location/java/com/android/internal/location/ProviderRequest.aidl19
-rw-r--r--location/java/com/android/internal/location/ProviderRequest.java94
-rw-r--r--location/lib/Android.mk3
-rw-r--r--location/lib/java/com/android/location/provider/LocationProvider.java358
-rw-r--r--location/lib/java/com/android/location/provider/LocationProviderBase.java184
-rw-r--r--location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java50
-rw-r--r--location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java53
-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
-rw-r--r--services/java/com/android/server/LocationManagerService.java1926
-rw-r--r--services/java/com/android/server/ServiceWatcher.java274
-rw-r--r--services/java/com/android/server/location/GeocoderProxy.java92
-rw-r--r--services/java/com/android/server/location/GeofenceManager.java188
-rw-r--r--services/java/com/android/server/location/GeofenceState.java (renamed from services/java/com/android/server/location/Geofence.java)40
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java549
-rw-r--r--services/java/com/android/server/location/LocationProviderInterface.java53
-rw-r--r--services/java/com/android/server/location/LocationProviderProxy.java570
-rw-r--r--services/java/com/android/server/location/MockProvider.java156
-rw-r--r--services/java/com/android/server/location/PassiveProvider.java94
42 files changed, 4357 insertions, 3123 deletions
diff --git a/Android.mk b/Android.mk
index 61e8a77..381989c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -197,8 +197,8 @@ LOCAL_SRC_FILES += \
location/java/android/location/IGpsStatusProvider.aidl \
location/java/android/location/ILocationListener.aidl \
location/java/android/location/ILocationManager.aidl \
- location/java/android/location/ILocationProvider.aidl \
location/java/android/location/INetInitiatedListener.aidl \
+ location/java/com/android/internal/location/ILocationProvider.aidl \
media/java/android/media/IAudioService.aidl \
media/java/android/media/IAudioFocusDispatcher.aidl \
media/java/android/media/IAudioRoutesObserver.aidl \
@@ -306,7 +306,11 @@ aidl_files := \
frameworks/base/graphics/java/android/graphics/Rect.aidl \
frameworks/base/graphics/java/android/graphics/Region.aidl \
frameworks/base/location/java/android/location/Criteria.aidl \
+ frameworks/base/location/java/android/location/Geofence.aidl \
frameworks/base/location/java/android/location/Location.aidl \
+ frameworks/base/location/java/android/location/LocationRequest.aidl \
+ frameworks/base/location/java/com/android/internal/location/ProviderProperties.aidl \
+ frameworks/base/location/java/com/android/internal/location/ProviderRequest.aidl \
frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
frameworks/base/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/api/current.txt b/api/current.txt
index c19acfe..e7dcf05 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10451,7 +10451,7 @@ package android.location {
field public static final android.os.Parcelable.Creator CREATOR;
}
- public class Criteria implements android.os.Parcelable {
+ public deprecated class Criteria implements android.os.Parcelable {
ctor public Criteria();
ctor public Criteria(android.location.Criteria);
method public int describeContents();
@@ -10497,6 +10497,13 @@ package android.location {
method public static boolean isPresent();
}
+ public final class Geofence implements android.os.Parcelable {
+ method public static android.location.Geofence createCircle(double, double, float);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public final class GpsSatellite {
method public float getAzimuth();
method public float getElevation();
@@ -10534,7 +10541,7 @@ package android.location {
method public int describeContents();
method public static void distanceBetween(double, double, double, double, float[]);
method public float distanceTo(android.location.Location);
- method public void dump(android.util.Printer, java.lang.String);
+ method public deprecated void dump(android.util.Printer, java.lang.String);
method public float getAccuracy();
method public double getAltitude();
method public float getBearing();
@@ -10582,65 +10589,96 @@ package android.location {
public class LocationManager {
method public boolean addGpsStatusListener(android.location.GpsStatus.Listener);
method public boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
- method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
- method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
- method public void clearTestProviderEnabled(java.lang.String);
- method public void clearTestProviderLocation(java.lang.String);
- method public void clearTestProviderStatus(java.lang.String);
- method public java.util.List<java.lang.String> getAllProviders();
- method public java.lang.String getBestProvider(android.location.Criteria, boolean);
+ method public deprecated void addProximityAlert(double, double, float, long, android.app.PendingIntent);
+ method public deprecated void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
+ method public deprecated void clearTestProviderEnabled(java.lang.String);
+ method public deprecated void clearTestProviderLocation(java.lang.String);
+ method public deprecated void clearTestProviderStatus(java.lang.String);
+ method public deprecated java.util.List<java.lang.String> getAllProviders();
+ method public deprecated java.lang.String getBestProvider(android.location.Criteria, boolean);
method public android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
- method public android.location.Location getLastKnownLocation(java.lang.String);
- method public android.location.LocationProvider getProvider(java.lang.String);
- method public java.util.List<java.lang.String> getProviders(boolean);
- method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
- method public boolean isProviderEnabled(java.lang.String);
+ method public deprecated android.location.Location getLastKnownLocation(java.lang.String);
+ method public deprecated android.location.Location getLastKnownLocation(android.location.Criteria);
+ method public android.location.Location getLastLocation(android.location.LocationRequest);
+ method public deprecated android.location.LocationProvider getProvider(java.lang.String);
+ method public deprecated java.util.List<java.lang.String> getProviders(boolean);
+ method public deprecated java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
+ method public deprecated boolean isProviderEnabled(java.lang.String);
+ method public void removeAllGeofences(android.app.PendingIntent);
+ method public void removeGeofence(android.location.Geofence, android.app.PendingIntent);
method public void removeGpsStatusListener(android.location.GpsStatus.Listener);
method public void removeNmeaListener(android.location.GpsStatus.NmeaListener);
- method public void removeProximityAlert(android.app.PendingIntent);
- method public void removeTestProvider(java.lang.String);
+ method public deprecated void removeProximityAlert(android.app.PendingIntent);
+ method public deprecated void removeTestProvider(java.lang.String);
method public void removeUpdates(android.location.LocationListener);
method public void removeUpdates(android.app.PendingIntent);
- method public void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener);
- method public void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener, android.os.Looper);
- method public void requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper);
- method public void requestLocationUpdates(java.lang.String, long, float, android.app.PendingIntent);
- method public void requestLocationUpdates(long, float, android.location.Criteria, android.app.PendingIntent);
- method public void requestSingleUpdate(java.lang.String, android.location.LocationListener, android.os.Looper);
- method public void requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper);
- method public void requestSingleUpdate(java.lang.String, android.app.PendingIntent);
- method public void requestSingleUpdate(android.location.Criteria, android.app.PendingIntent);
- method public boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle);
- method public void setTestProviderEnabled(java.lang.String, boolean);
- method public void setTestProviderLocation(java.lang.String, android.location.Location);
- method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
- field public static final java.lang.String GPS_PROVIDER = "gps";
+ method public void requestGeofence(android.location.LocationRequest, android.location.Geofence, android.app.PendingIntent);
+ method public deprecated void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener);
+ method public deprecated void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener, android.os.Looper);
+ method public deprecated void requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper);
+ method public deprecated void requestLocationUpdates(java.lang.String, long, float, android.app.PendingIntent);
+ method public deprecated void requestLocationUpdates(long, float, android.location.Criteria, android.app.PendingIntent);
+ method public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
+ method public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
+ method public deprecated void requestSingleUpdate(java.lang.String, android.location.LocationListener, android.os.Looper);
+ method public deprecated void requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper);
+ method public deprecated void requestSingleUpdate(java.lang.String, android.app.PendingIntent);
+ method public deprecated void requestSingleUpdate(android.location.Criteria, android.app.PendingIntent);
+ method public deprecated boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle);
+ method public deprecated void setTestProviderEnabled(java.lang.String, boolean);
+ method public deprecated void setTestProviderLocation(java.lang.String, android.location.Location);
+ method public deprecated void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+ field public static final deprecated java.lang.String GPS_PROVIDER = "gps";
field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
- field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
+ field public static final deprecated java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
field public static final java.lang.String KEY_PROXIMITY_ENTERING = "entering";
- field public static final java.lang.String KEY_STATUS_CHANGED = "status";
- field public static final java.lang.String NETWORK_PROVIDER = "network";
- field public static final java.lang.String PASSIVE_PROVIDER = "passive";
- field public static final java.lang.String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
+ field public static final deprecated java.lang.String KEY_STATUS_CHANGED = "status";
+ field public static final deprecated java.lang.String NETWORK_PROVIDER = "network";
+ field public static final deprecated java.lang.String PASSIVE_PROVIDER = "passive";
+ field public static final deprecated java.lang.String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
}
- public abstract class LocationProvider {
- method public abstract int getAccuracy();
+ public deprecated class LocationProvider {
+ method public int getAccuracy();
method public java.lang.String getName();
- method public abstract int getPowerRequirement();
- method public abstract boolean hasMonetaryCost();
+ method public int getPowerRequirement();
+ method public boolean hasMonetaryCost();
method public boolean meetsCriteria(android.location.Criteria);
- method public abstract boolean requiresCell();
- method public abstract boolean requiresNetwork();
- method public abstract boolean requiresSatellite();
- method public abstract boolean supportsAltitude();
- method public abstract boolean supportsBearing();
- method public abstract boolean supportsSpeed();
+ method public boolean requiresCell();
+ method public boolean requiresNetwork();
+ method public boolean requiresSatellite();
+ method public boolean supportsAltitude();
+ method public boolean supportsBearing();
+ method public boolean supportsSpeed();
field public static final int AVAILABLE = 2; // 0x2
field public static final int OUT_OF_SERVICE = 0; // 0x0
field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
}
+ public final class LocationRequest implements android.os.Parcelable {
+ method public static android.location.LocationRequest create();
+ method public int describeContents();
+ method public long getExpireAt();
+ method public long getFastestInterval();
+ method public long getInterval();
+ method public int getNumUpdates();
+ method public int getQuality();
+ method public android.location.LocationRequest setExpireAt(long);
+ method public android.location.LocationRequest setExpireIn(long);
+ method public android.location.LocationRequest setFastestInterval(long);
+ method public android.location.LocationRequest setInterval(long);
+ method public android.location.LocationRequest setNumUpdates(int);
+ method public android.location.LocationRequest setQuality(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACCURACY_BLOCK = 102; // 0x66
+ field public static final int ACCURACY_CITY = 104; // 0x68
+ field public static final int ACCURACY_FINE = 100; // 0x64
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int POWER_HIGH = 203; // 0xcb
+ field public static final int POWER_LOW = 201; // 0xc9
+ field public static final int POWER_NONE = 200; // 0xc8
+ }
+
}
package android.media {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a4bf43f..6ab323e 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -592,11 +592,30 @@
<!-- True if WallpaperService is enabled -->
<bool name="config_enableWallpaperService">true</bool>
- <!-- Package name providing network location support. -->
- <string name="config_networkLocationProviderPackageName" translatable="false">@null</string>
+ <!-- Package name(s) containing location provider support.
+ These packages can contain services implementing location providers,
+ such as the Geocode Provider, Network Location Provider, and
+ Fused Location Provider. They will each be searched for
+ service components implementing these providers.
+ It is strongly recommended that the packages explicitly named
+ below are on the system image, so that they will not map to
+ a 3rd party application.
+ The location framework also has support for installation
+ of new location providers at run-time. The new package does not
+ have to be explicitly listed here, however it must have a signature
+ that matches the signature of at least one package on this list.
+ Platforms should overlay additional packages in
+ config_overlay_locationProviderPackageNames, instead of overlaying
+ this config, if they only want to append packages and not replace
+ the entire array.
+ -->
+ <string-array name="config_locationProviderPackageNames" translatable="false">
+ <item>com.android.location.fused</item>
+ </string-array>
- <!-- Package name providing geocoder API support. -->
- <string name="config_geocodeProviderPackageName" translatable="false">@null</string>
+ <!-- Pacakge name(s) supplied by overlay, and appended to
+ config_locationProviderPackageNames. -->
+ <string-array name="config_overlay_locationProviderPackageNames" translatable="false" />
<!-- Boolean indicating if current platform supports bluetooth SCO for off call
use cases -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 965d02b..5b86db5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1415,6 +1415,8 @@
<java-symbol type="array" name="config_serialPorts" />
<java-symbol type="array" name="radioAttributes" />
<java-symbol type="array" name="config_oemUsbModeOverride" />
+ <java-symbol type="array" name="config_locationProviderPackageNames" />
+ <java-symbol type="array" name="config_overlay_locationProviderPackageNames" />
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
<java-symbol type="bool" name="config_sf_limitedAlpha" />
@@ -1493,8 +1495,6 @@
<java-symbol type="string" name="car_mode_disable_notification_title" />
<java-symbol type="string" name="chooser_wallpaper" />
<java-symbol type="string" name="config_datause_iface" />
- <java-symbol type="string" name="config_geocodeProviderPackageName" />
- <java-symbol type="string" name="config_networkLocationProviderPackageName" />
<java-symbol type="string" name="config_wimaxManagerClassname" />
<java-symbol type="string" name="config_wimaxNativeLibLocation" />
<java-symbol type="string" name="config_wimaxServiceClassname" />
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index 1f3fb7a..6258a43 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -24,7 +24,9 @@ import android.os.Parcelable;
* location provider. Providers maybe ordered according to accuracy,
* power usage, ability to report altitude, speed,
* and bearing, and monetary cost.
+ * @deprecated {@link LocationRequest} instead
*/
+@Deprecated
public class Criteria implements Parcelable {
/**
* A constant indicating that the application does not choose to
@@ -326,6 +328,7 @@ public class Criteria implements Parcelable {
public static final Parcelable.Creator<Criteria> CREATOR =
new Parcelable.Creator<Criteria>() {
+ @Override
public Criteria createFromParcel(Parcel in) {
Criteria c = new Criteria();
c.mHorizontalAccuracy = in.readInt();
@@ -340,15 +343,18 @@ public class Criteria implements Parcelable {
return c;
}
+ @Override
public Criteria[] newArray(int size) {
return new Criteria[size];
}
};
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mHorizontalAccuracy);
parcel.writeInt(mVerticalAccuracy);
@@ -360,4 +366,43 @@ public class Criteria implements Parcelable {
parcel.writeInt(mSpeedRequired ? 1 : 0);
parcel.writeInt(mCostAllowed ? 1 : 0);
}
+
+ private static String powerToString(int power) {
+ switch (power) {
+ case NO_REQUIREMENT:
+ return "NO_REQ";
+ case POWER_LOW:
+ return "LOW";
+ case POWER_MEDIUM:
+ return "MEDIUM";
+ case POWER_HIGH:
+ return "HIGH";
+ default:
+ return "???";
+ }
+ }
+
+ private static String accuracyToString(int accuracy) {
+ switch (accuracy) {
+ case NO_REQUIREMENT:
+ return "---";
+ case ACCURACY_HIGH:
+ return "HIGH";
+ case ACCURACY_MEDIUM:
+ return "MEDIUM";
+ case ACCURACY_LOW:
+ return "LOW";
+ default:
+ return "???";
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Criteria[power=").append(powerToString(mPowerRequirement));
+ s.append(" acc=").append(accuracyToString(mHorizontalAccuracy));
+ s.append(']');
+ return s.toString();
+ }
}
diff --git a/location/java/android/location/Geofence.aidl b/location/java/android/location/Geofence.aidl
new file mode 100644
index 0000000..a5c6aa0
--- /dev/null
+++ b/location/java/android/location/Geofence.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable Geofence;
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
new file mode 100644
index 0000000..353a1ca
--- /dev/null
+++ b/location/java/android/location/Geofence.java
@@ -0,0 +1,174 @@
+/*
+ * 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;
+
+/**
+ * Represents a Geofence
+ */
+public final class Geofence implements Parcelable {
+ /** @hide */
+ public static final int TYPE_HORIZONTAL_CIRCLE = 1;
+
+ private final int mType;
+ private final double mLatitude;
+ private final double mLongitude;
+ private final float mRadius;
+
+ /**
+ * Create a horizontal, circular geofence.
+ * @param latitude latitude in degrees
+ * @param longitude longitude in degrees
+ * @param radius radius in meters
+ * @return a new geofence
+ * @throws IllegalArgumentException if any parameters are out of range
+ */
+ public static Geofence createCircle(double latitude, double longitude, float radius) {
+ return new Geofence(latitude, longitude, radius);
+ }
+
+ private Geofence(double latitude, double longitude, float radius) {
+ checkRadius(radius);
+ checkLatLong(latitude, longitude);
+ mType = TYPE_HORIZONTAL_CIRCLE;
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mRadius = radius;
+ }
+
+ /** @hide */
+ public int getType() {
+ return mType;
+ }
+
+ /** @hide */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /** @hide */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /** @hide */
+ public float getRadius() {
+ return mRadius;
+ }
+
+ private static void checkRadius(float radius) {
+ if (radius <= 0) {
+ throw new IllegalArgumentException("invalid radius: " + radius);
+ }
+ }
+
+ private static void checkLatLong(double latitude, double longitude) {
+ if (latitude > 90.0 || latitude < -90.0) {
+ throw new IllegalArgumentException("invalid latitude: " + latitude);
+ }
+ if (longitude > 180.0 || longitude < -180.0) {
+ throw new IllegalArgumentException("invalid longitude: " + longitude);
+ }
+ }
+
+ private static void checkType(int type) {
+ if (type != TYPE_HORIZONTAL_CIRCLE) {
+ throw new IllegalArgumentException("invalid type: " + type);
+ }
+ }
+
+ public static final Parcelable.Creator<Geofence> CREATOR = new Parcelable.Creator<Geofence>() {
+ @Override
+ public Geofence createFromParcel(Parcel in) {
+ int type = in.readInt();
+ double latitude = in.readDouble();
+ double longitude = in.readDouble();
+ float radius = in.readFloat();
+ checkType(type);
+ return Geofence.createCircle(latitude, longitude, radius);
+ }
+ @Override
+ public Geofence[] newArray(int size) {
+ return new Geofence[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeDouble(mLatitude);
+ parcel.writeDouble(mLongitude);
+ parcel.writeFloat(mRadius);
+ }
+
+ private static String typeToString(int type) {
+ switch (type) {
+ case TYPE_HORIZONTAL_CIRCLE:
+ return "CIRCLE";
+ default:
+ checkType(type);
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Geofence[%s %.6f, %.6f %.0fm]",
+ typeToString(mType), mLatitude, mLongitude, mRadius);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(mLatitude);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(mLongitude);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + Float.floatToIntBits(mRadius);
+ result = prime * result + mType;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (!(obj instanceof Geofence))
+ return false;
+ Geofence other = (Geofence) obj;
+ if (mRadius != other.mRadius)
+ return false;
+ if (mLatitude != other.mLatitude)
+ return false;
+ if (mLongitude != other.mLongitude)
+ return false;
+ if (mType != other.mType)
+ return false;
+ return true;
+ }
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 47b7adf..a2ce606 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -20,53 +20,36 @@ import android.app.PendingIntent;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
+import android.location.Geofence;
import android.location.IGeocodeProvider;
import android.location.IGpsStatusListener;
import android.location.ILocationListener;
import android.location.Location;
+import android.location.LocationRequest;
import android.os.Bundle;
+import com.android.internal.location.ProviderProperties;
+
/**
* System private API for talking with the location service.
*
- * {@hide}
+ * @hide
*/
interface ILocationManager
{
- 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(in LocationRequest request, in ILocationListener listener,
+ in PendingIntent intent, String packageName);
+ void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName);
+
+ void requestGeofence(in LocationRequest request, in Geofence geofence,
+ in PendingIntent intent, String packageName);
+ void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
- 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);
+ Location getLastLocation(in LocationRequest request);
boolean addGpsStatusListener(IGpsStatusListener listener);
void removeGpsStatusListener(IGpsStatusListener listener);
- // for reporting callback completion
- void locationCallbackFinished(ILocationListener listener);
-
- boolean sendExtraCommand(String provider, String command, inout Bundle extras);
-
- void addProximityAlert(double latitude, double longitude, float distance,
- long expiration, in PendingIntent intent, in String packageName);
- void removeProximityAlert(in PendingIntent intent);
-
- Bundle getProviderInfo(String provider);
- boolean isProviderEnabled(String provider);
-
- Location getLastKnownLocation(String provider);
-
- // Used by location providers to tell the location manager when it has a new location.
- // Passive is true if the location is coming from the passive provider, in which case
- // it need not be shared with other providers.
- void reportLocation(in Location location, boolean passive);
-
boolean geocoderIsPresent();
String getFromLocation(double latitude, double longitude, int maxResults,
in GeocoderParams params, out List<Address> addrs);
@@ -75,9 +58,17 @@ interface ILocationManager
double upperRightLatitude, double upperRightLongitude, int maxResults,
in GeocoderParams params, out List<Address> addrs);
- void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy);
+ boolean sendNiResponse(int notifId, int userResponse);
+
+ // --- deprecated ---
+ 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);
+ ProviderProperties getProviderProperties(String provider);
+ boolean isProviderEnabled(String provider);
+
+ void addTestProvider(String name, in ProviderProperties properties);
void removeTestProvider(String provider);
void setTestProviderLocation(String provider, in Location loc);
void clearTestProviderLocation(String provider);
@@ -86,6 +77,17 @@ interface ILocationManager
void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime);
void clearTestProviderStatus(String provider);
- // for NI support
- boolean sendNiResponse(int notifId, int userResponse);
+ boolean sendExtraCommand(String provider, String command, inout Bundle extras);
+
+ // --- internal ---
+
+ // Used by location providers to tell the location manager when it has a new location.
+ // Passive is true if the location is coming from the passive provider, in which case
+ // it need not be shared with other providers.
+ void reportLocation(in Location location, boolean passive);
+
+ // for reporting callback completion
+ void locationCallbackFinished(ILocationListener listener);
+
+
}
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 5ad60ab..ece4500 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.Printer;
+import android.util.TimeUtils;
import java.text.DecimalFormat;
import java.util.StringTokenizer;
@@ -84,17 +85,6 @@ public class Location implements Parcelable {
// Scratchpad
private float[] mResults = new float[2];
- public void dump(Printer pw, String prefix) {
- pw.println(prefix + "mProvider=" + mProvider + " mTime=" + mTime);
- pw.println(prefix + "mElapsedRealtimeNano=" + mElapsedRealtimeNano);
- pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
- pw.println(prefix + "mHasAltitude=" + mHasAltitude + " mAltitude=" + mAltitude);
- pw.println(prefix + "mHasSpeed=" + mHasSpeed + " mSpeed=" + mSpeed);
- pw.println(prefix + "mHasBearing=" + mHasBearing + " mBearing=" + mBearing);
- pw.println(prefix + "mHasAccuracy=" + mHasAccuracy + " mAccuracy=" + mAccuracy);
- pw.println(prefix + "mExtras=" + mExtras);
- }
-
/**
* Constructs a new Location. By default, time, latitude,
* longitude, and numSatellites are 0; hasAltitude, hasSpeed, and
@@ -766,25 +756,46 @@ public class Location implements Parcelable {
mExtras = (extras == null) ? null : new Bundle(extras);
}
- @Override public String toString() {
- return "Location[mProvider=" + mProvider +
- ",mTime=" + mTime +
- ",mElapsedRealtimeNano=" + mElapsedRealtimeNano +
- ",mLatitude=" + mLatitude +
- ",mLongitude=" + mLongitude +
- ",mHasAltitude=" + mHasAltitude +
- ",mAltitude=" + mAltitude +
- ",mHasSpeed=" + mHasSpeed +
- ",mSpeed=" + mSpeed +
- ",mHasBearing=" + mHasBearing +
- ",mBearing=" + mBearing +
- ",mHasAccuracy=" + mHasAccuracy +
- ",mAccuracy=" + mAccuracy +
- ",mExtras=" + mExtras + "]";
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Location[");
+ s.append(mProvider);
+ s.append(String.format(" %.6f,%.6f", mLatitude, mLongitude));
+ if (mHasAccuracy) s.append(String.format(" acc=%.0f", mAccuracy));
+ else s.append(" acc=???");
+ if (mTime == 0) {
+ s.append(" t=?!?");
+ }
+ if (mElapsedRealtimeNano == 0) {
+ s.append(" et=?!?");
+ } else {
+ long age = SystemClock.elapsedRealtimeNano() - mElapsedRealtimeNano;
+ s.append(" age=");
+ TimeUtils.formatDuration(age / 1000000L, s);
+ }
+ if (mHasAltitude) s.append(" alt=").append(mAltitude);
+ if (mHasSpeed) s.append(" vel=").append(mSpeed);
+ if (mHasBearing) s.append(" bear=").append(mBearing);
+
+ if (mExtras != null) {
+ s.append(" {").append(mExtras).append('}');
+ }
+ s.append(']');
+ return s.toString();
+ }
+
+ /**
+ * @deprecated Use {@link #toString} instead
+ */
+ @Deprecated
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + toString());
}
public static final Parcelable.Creator<Location> CREATOR =
new Parcelable.Creator<Location>() {
+ @Override
public Location createFromParcel(Parcel in) {
String provider = in.readString();
Location l = new Location(provider);
@@ -804,15 +815,18 @@ public class Location implements Parcelable {
return l;
}
+ @Override
public Location[] newArray(int size) {
return new Location[size];
}
};
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mProvider);
parcel.writeLong(mTime);
@@ -828,5 +842,5 @@ public class Location implements Parcelable {
parcel.writeInt(mHasAccuracy ? 1 : 0);
parcel.writeFloat(mAccuracy);
parcel.writeBundle(mExtras);
- }
+ }
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 15a2928..083b900 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -25,15 +25,15 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.Handler;
import android.os.Message;
-import android.os.SystemClock;
import android.util.Log;
-import com.android.internal.location.DummyLocationProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import com.android.internal.location.ProviderProperties;
+
/**
* This class provides access to the system location services. These
* services allow applications to obtain periodic updates of the
@@ -71,7 +71,9 @@ public class LocationManager {
*
* Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
* or android.permission.ACCESS_FINE_LOCATION.
+ * @deprecated use the {@link Criteria} class instead
*/
+ @Deprecated
public static final String NETWORK_PROVIDER = "network";
/**
@@ -87,7 +89,9 @@ public class LocationManager {
* <ul>
* <li> satellites - the number of satellites used to derive the fix
* </ul>
+ * @deprecated use the {@link Criteria} class instead
*/
+ @Deprecated
public static final String GPS_PROVIDER = "gps";
/**
@@ -100,10 +104,22 @@ public class LocationManager {
*
* Requires the permission android.permission.ACCESS_FINE_LOCATION, although if the GPS
* is not enabled this provider might only return coarse fixes.
+ * @deprecated use the {@link Criteria} class instead
*/
+ @Deprecated
public static final String PASSIVE_PROVIDER = "passive";
/**
+ * Name of the Fused location provider.<p>
+ * This provider combines inputs for all possible location sources
+ * to provide the best possible Location fix.<p>
+ *
+ * Requires the permission android.permission.ACCESS_FINE_LOCATION.
+ * @hide
+ */
+ public static final String FUSED_PROVIDER = "fused";
+
+ /**
* Key used for the Bundle extra holding a boolean indicating whether
* a proximity alert is entering (true) or exiting (false)..
*/
@@ -112,13 +128,17 @@ public class LocationManager {
/**
* Key used for a Bundle extra holding an Integer status value
* when a status change is broadcast using a PendingIntent.
+ * @deprecated use the {@link Criteria} class instead
*/
+ @Deprecated
public static final String KEY_STATUS_CHANGED = "status";
/**
* Key used for a Bundle extra holding an Boolean status value
* when a provider enabled/disabled event is broadcast using a PendingIntent.
+ * @deprecated use the {@link Criteria} class instead
*/
+ @Deprecated
public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
/**
@@ -141,7 +161,9 @@ public class LocationManager {
/**
* Broadcast intent action when the configured location providers
* change.
+ * @deprecated use the {@link Criteria} class instead
*/
+ @Deprecated
public static final String PROVIDERS_CHANGED_ACTION =
"android.location.PROVIDERS_CHANGED";
@@ -274,19 +296,8 @@ public class LocationManager {
mContext = context;
}
- private LocationProvider createProvider(String name, Bundle info) {
- DummyLocationProvider provider =
- new DummyLocationProvider(name, mService);
- provider.setRequiresNetwork(info.getBoolean("network"));
- provider.setRequiresSatellite(info.getBoolean("satellite"));
- provider.setRequiresCell(info.getBoolean("cell"));
- provider.setHasMonetaryCost(info.getBoolean("cost"));
- provider.setSupportsAltitude(info.getBoolean("altitude"));
- provider.setSupportsSpeed(info.getBoolean("speed"));
- provider.setSupportsBearing(info.getBoolean("bearing"));
- provider.setPowerRequirement(info.getInt("power"));
- provider.setAccuracy(info.getInt("accuracy"));
- return provider;
+ private LocationProvider createProvider(String name, ProviderProperties properties) {
+ return new LocationProvider(name, properties);
}
/**
@@ -295,15 +306,14 @@ public class LocationManager {
* accessed by the calling activity or are currently disabled.
*
* @return list of Strings containing names of the providers
+ * @deprecated use the {@link Criteria} class instead
*/
+ @Deprecated
public List<String> getAllProviders() {
- if (false) {
- Log.d(TAG, "getAllProviders");
- }
try {
return mService.getAllProviders();
- } catch (RemoteException ex) {
- Log.e(TAG, "getAllProviders: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
return null;
}
@@ -315,12 +325,16 @@ public class LocationManager {
* @param enabledOnly if true then only the providers which are currently
* enabled are returned.
* @return list of Strings containing names of the providers
+ * @deprecated The {@link LocationProvider} class is deprecated. So
+ * use the {@link Criteria} class to request location instead of
+ * enumerating providers.
*/
+ @Deprecated
public List<String> getProviders(boolean enabledOnly) {
try {
return mService.getProviders(null, enabledOnly);
- } catch (RemoteException ex) {
- Log.e(TAG, "getProviders: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
return null;
}
@@ -332,22 +346,24 @@ public class LocationManager {
* @param name the provider name
* @return a LocationProvider, or null
*
- * @throws IllegalArgumentException if name is null
+ * @throws IllegalArgumentException if name is null or does not exisit
* @throws SecurityException if the caller is not permitted to access the
* given provider.
+ * @deprecated The {@link LocationProvider} class is deprecated. So
+ * use the {@link Criteria} class to request location instead of
+ * enumerating providers.
*/
+ @Deprecated
public LocationProvider getProvider(String name) {
- if (name == null) {
- throw new IllegalArgumentException("name==null");
- }
+ checkProvider(name);
try {
- Bundle info = mService.getProviderInfo(name);
- if (info == null) {
+ ProviderProperties properties = mService.getProviderProperties(name);
+ if (properties == null) {
return null;
}
- return createProvider(name, info);
- } catch (RemoteException ex) {
- Log.e(TAG, "getProvider: RemoteException", ex);
+ return createProvider(name, properties);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
return null;
}
@@ -361,15 +377,17 @@ public class LocationManager {
* @param enabledOnly if true then only the providers which are currently
* enabled are returned.
* @return list of Strings containing names of the providers
+ * @deprecated The {@link LocationProvider} class is deprecated. So
+ * use the {@link Criteria} class to request location instead of
+ * enumerating providers.
*/
+ @Deprecated
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
- if (criteria == null) {
- throw new IllegalArgumentException("criteria==null");
- }
+ checkCriteria(criteria);
try {
return mService.getProviders(criteria, enabledOnly);
- } catch (RemoteException ex) {
- Log.e(TAG, "getProviders: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
return null;
}
@@ -395,15 +413,15 @@ public class LocationManager {
* @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
+ * @deprecated using an explicit provider doesn't allow fused location
*/
+ @Deprecated
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- if (criteria == null) {
- throw new IllegalArgumentException("criteria==null");
- }
+ checkCriteria(criteria);
try {
return mService.getBestProvider(criteria, enabledOnly);
- } catch (RemoteException ex) {
- Log.e(TAG, "getBestProvider: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
return null;
}
@@ -480,16 +498,17 @@ public class LocationManager {
* @throws IllegalArgumentException if listener is null
* @throws RuntimeException if the calling thread has no Looper
* @throws SecurityException if no suitable permission is present for the provider.
+ * @deprecated use the {@link LocationRequest} class instead
*/
- public void requestLocationUpdates(String provider,
- long minTime, float minDistance, LocationListener listener) {
- if (provider == null) {
- throw new IllegalArgumentException("provider==null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("listener==null");
- }
- _requestLocationUpdates(provider, null, minTime, minDistance, false, listener, null);
+ @Deprecated
+ public void requestLocationUpdates(String provider, long minTime, float minDistance,
+ LocationListener listener) {
+ checkProvider(provider);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTime, minDistance, false);
+ requestLocationUpdates(request, listener, null, null);
}
/**
@@ -564,17 +583,17 @@ public class LocationManager {
* @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.
+ * @deprecated use the {@link LocationRequest} class instead
*/
- public void requestLocationUpdates(String provider,
- long minTime, float minDistance, LocationListener listener,
- Looper looper) {
- if (provider == null) {
- throw new IllegalArgumentException("provider==null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("listener==null");
- }
- _requestLocationUpdates(provider, null, minTime, minDistance, false, listener, looper);
+ @Deprecated
+ public void requestLocationUpdates(String provider, long minTime, float minDistance,
+ LocationListener listener, Looper looper) {
+ checkProvider(provider);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTime, minDistance, false);
+ requestLocationUpdates(request, listener, looper, null);
}
/**
@@ -639,39 +658,17 @@ public class LocationManager {
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present for the provider.
+ * @deprecated use the {@link LocationRequest} class instead
*/
- 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;
- }
- if (minDistance < 0.0f) {
- minDistance = 0.0f;
- }
+ @Deprecated
+ public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
+ LocationListener listener, Looper looper) {
+ checkCriteria(criteria);
+ checkListener(listener);
- try {
- synchronized (mListeners) {
- ListenerTransport transport = mListeners.get(listener);
- if (transport == null) {
- transport = new ListenerTransport(listener, looper);
- }
- mListeners.put(listener, transport);
- mService.requestLocationUpdates(provider, criteria, minTime, minDistance, singleShot, transport);
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
- }
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTime, minDistance, false);
+ requestLocationUpdates(request, listener, looper, null);
}
/**
@@ -749,16 +746,17 @@ public class LocationManager {
* on this device
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present for the provider.
+ * @deprecated use the {@link LocationRequest} class instead
*/
- public void requestLocationUpdates(String provider,
- long minTime, float minDistance, PendingIntent intent) {
- if (provider == null) {
- throw new IllegalArgumentException("provider==null");
- }
- if (intent == null) {
- throw new IllegalArgumentException("intent==null");
- }
- _requestLocationUpdates(provider, null, minTime, minDistance, false, intent);
+ @Deprecated
+ public void requestLocationUpdates(String provider, long minTime, float minDistance,
+ PendingIntent intent) {
+ checkProvider(provider);
+ checkPendingIntent(intent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTime, minDistance, false);
+ requestLocationUpdates(request, null, null, intent);
}
/**
@@ -825,32 +823,17 @@ public class LocationManager {
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present for the provider.
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
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;
- }
- if (minDistance < 0.0f) {
- minDistance = 0.0f;
- }
+ checkCriteria(criteria);
+ checkPendingIntent(intent);
- try {
- mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, intent);
- } catch (RemoteException ex) {
- Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
- }
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTime, minDistance, false);
+ requestLocationUpdates(request, null, null, intent);
}
/**
@@ -879,15 +862,16 @@ public class LocationManager {
* @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
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
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);
+ checkProvider(provider);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+ requestLocationUpdates(request, listener, looper, null);
}
/**
@@ -918,15 +902,16 @@ public class LocationManager {
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present to access
* the location services
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
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);
+ checkCriteria(criteria);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, 0, 0, true);
+ requestLocationUpdates(request, listener, looper, null);
}
/**
@@ -951,15 +936,16 @@ public class LocationManager {
* @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
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
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);
+ checkProvider(provider);
+ checkPendingIntent(intent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+ requestLocationUpdates(request, null, null, intent);
}
/**
@@ -986,15 +972,54 @@ public class LocationManager {
* @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
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
- if (criteria == null) {
- throw new IllegalArgumentException("criteria==null");
- }
- if (intent == null) {
- throw new IllegalArgumentException("intent==null");
+ checkCriteria(criteria);
+ checkPendingIntent(intent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, 0, 0, true);
+ requestLocationUpdates(request, null, null, intent);
+ }
+
+ public void requestLocationUpdates(LocationRequest request, LocationListener listener,
+ Looper looper) {
+ checkListener(listener);
+ requestLocationUpdates(request, listener, looper, null);
+ }
+
+ public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
+ checkPendingIntent(intent);
+ requestLocationUpdates(request, null, null, intent);
+ }
+
+ private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
+ if (listener == null) return null;
+ synchronized (mListeners) {
+ ListenerTransport transport = mListeners.get(listener);
+ if (transport == null) {
+ transport = new ListenerTransport(listener, looper);
+ }
+ mListeners.put(listener, transport);
+ return transport;
}
- _requestLocationUpdates(null, criteria, 0L, 0.0f, true, intent);
+ }
+
+ private void requestLocationUpdates(LocationRequest request, LocationListener listener,
+ Looper looper, PendingIntent intent) {
+
+ String packageName = mContext.getPackageName();
+
+ // wrap the listener class
+ ListenerTransport transport = wrapListener(listener, looper);
+
+ try {
+ mService.requestLocationUpdates(request, transport, intent, packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
+ }
}
/**
@@ -1006,19 +1031,19 @@ public class LocationManager {
* @throws IllegalArgumentException if listener is null
*/
public void removeUpdates(LocationListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener==null");
- }
- if (false) {
- Log.d(TAG, "removeUpdates: listener = " + listener);
+ checkListener(listener);
+ String packageName = mContext.getPackageName();
+
+ ListenerTransport transport;
+ synchronized (mListeners) {
+ transport = mListeners.remove(listener);
}
+ if (transport == null) return;
+
try {
- ListenerTransport transport = mListeners.remove(listener);
- if (transport != null) {
- mService.removeUpdates(transport);
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "removeUpdates: DeadObjectException", ex);
+ mService.removeUpdates(transport, null, packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1031,16 +1056,13 @@ public class LocationManager {
* @throws IllegalArgumentException if intent is null
*/
public void removeUpdates(PendingIntent intent) {
- if (intent == null) {
- throw new IllegalArgumentException("intent==null");
- }
- if (false) {
- Log.d(TAG, "removeUpdates: intent = " + intent);
- }
+ checkPendingIntent(intent);
+ String packageName = mContext.getPackageName();
+
try {
- mService.removeUpdatesPI(intent);
- } catch (RemoteException ex) {
- Log.e(TAG, "removeUpdates: RemoteException", ex);
+ mService.removeUpdates(null, intent, packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1086,20 +1108,31 @@ public class LocationManager {
*
* @throws SecurityException if no permission exists for the required
* providers.
+ * @deprecated use the {@link LocationRequest} class instead
*/
- public void addProximityAlert(double latitude, double longitude,
- float radius, long expiration, PendingIntent intent) {
- if (false) {
- Log.d(TAG, "addProximityAlert: latitude = " + latitude +
- ", longitude = " + longitude + ", radius = " + radius +
- ", expiration = " + expiration +
- ", intent = " + intent);
+ @Deprecated
+ public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
+ PendingIntent intent) {
+ checkPendingIntent(intent);
+ if (expiration < 0) expiration = Long.MAX_VALUE;
+
+ Geofence fence = Geofence.createCircle(latitude, longitude, radius);
+ LocationRequest request = new LocationRequest().setExpireIn(expiration);
+ try {
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
+ }
+
+ public void requestGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
+ checkPendingIntent(intent);
+ checkGeofence(fence);
+
try {
- mService.addProximityAlert(latitude, longitude, radius, expiration, intent,
- mContext.getPackageName());
- } catch (RemoteException ex) {
- Log.e(TAG, "addProximityAlert: RemoteException", ex);
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1108,15 +1141,40 @@ public class LocationManager {
*
* @param intent the PendingIntent that no longer needs to be notified of
* proximity alerts
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void removeProximityAlert(PendingIntent intent) {
- if (false) {
- Log.d(TAG, "removeProximityAlert: intent = " + intent);
+ checkPendingIntent(intent);
+ String packageName = mContext.getPackageName();
+
+ try {
+ mService.removeGeofence(null, intent, packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
+ }
+
+ public void removeGeofence(Geofence fence, PendingIntent intent) {
+ checkPendingIntent(intent);
+ checkGeofence(fence);
+ String packageName = mContext.getPackageName();
+
try {
- mService.removeProximityAlert(intent);
- } catch (RemoteException ex) {
- Log.e(TAG, "removeProximityAlert: RemoteException", ex);
+ mService.removeGeofence(fence, intent, packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
+ }
+ }
+
+ public void removeAllGeofences(PendingIntent intent) {
+ checkPendingIntent(intent);
+ String packageName = mContext.getPackageName();
+
+ try {
+ mService.removeGeofence(null, intent, packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1130,19 +1188,30 @@ public class LocationManager {
*
* @throws SecurityException if no suitable permission is present for the provider.
* @throws IllegalArgumentException if provider is null
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public boolean isProviderEnabled(String provider) {
- if (provider == null) {
- throw new IllegalArgumentException("provider==null");
- }
+ checkProvider(provider);
+
try {
return mService.isProviderEnabled(provider);
- } catch (RemoteException ex) {
- Log.e(TAG, "isProviderEnabled: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
return false;
}
}
+ public Location getLastLocation(LocationRequest request) {
+ try {
+ return mService.getLastLocation(request);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
+ return null;
+ }
+ }
+
+
/**
* Returns a Location indicating the data from the last known
* location fix obtained from the given provider. This can be done
@@ -1157,15 +1226,49 @@ public class LocationManager {
*
* @throws SecurityException if no suitable permission is present for the provider.
* @throws IllegalArgumentException if provider is null or doesn't exist
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public Location getLastKnownLocation(String provider) {
- if (provider == null) {
- throw new IllegalArgumentException("provider==null");
+ checkProvider(provider);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+
+ try {
+ return mService.getLastLocation(request);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
+ return null;
}
+ }
+
+ /**
+ * Return the last know Location that satisfies the given
+ * criteria. This can be done without starting the provider.
+ * Note that this location could
+ * be out-of-date, for example if the device was turned off and
+ * moved to another location.
+ *
+ * <p> If no location is found that satisfies the criteria, null is returned
+ *
+ * @param criteria location criteria
+ * @return the last known location that satisfies criteria, or null
+ *
+ * @throws SecurityException if no suitable permission is present
+ * @throws IllegalArgumentException if criteria is null
+ * @deprecated use the {@link LocationRequest} class instead
+ */
+ @Deprecated
+ public Location getLastKnownLocation(Criteria criteria) {
+ checkCriteria(criteria);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, 0, 0, true);
try {
- return mService.getLastKnownLocation(provider);
- } catch (RemoteException ex) {
- Log.e(TAG, "getLastKnowLocation: RemoteException", ex);
+ return mService.getLastLocation(request);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
return null;
}
}
@@ -1190,16 +1293,23 @@ public class LocationManager {
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
* @throws IllegalArgumentException if a provider with the given name already exists
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ ProviderProperties properties = new ProviderProperties(requiresNetwork,
+ requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
+ supportsBearing, powerRequirement, accuracy);
+ if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
+ throw new IllegalArgumentException("provider name contains illegal character: " + name);
+ }
+
try {
- mService.addTestProvider(name, requiresNetwork, requiresSatellite, requiresCell,
- hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement,
- accuracy);
- } catch (RemoteException ex) {
- Log.e(TAG, "addTestProvider: RemoteException", ex);
+ mService.addTestProvider(name, properties);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1212,12 +1322,14 @@ public class LocationManager {
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void removeTestProvider(String provider) {
try {
mService.removeTestProvider(provider);
- } catch (RemoteException ex) {
- Log.e(TAG, "removeTestProvider: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1236,23 +1348,27 @@ public class LocationManager {
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
* @throws IllegalArgumentException if the location is incomplete
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void setTestProviderLocation(String provider, Location loc) {
if (!loc.isComplete()) {
+ IllegalArgumentException e = new IllegalArgumentException(
+ "Incomplete location object, missing timestamp or accuracy? " + loc);
if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
- // for backwards compatibility, allow mock locations that are incomplete
- Log.w(TAG, "Incomplete Location object", new Throwable());
+ // just log on old platform (for backwards compatibility)
+ Log.w(TAG, e);
loc.makeComplete();
} else {
- throw new IllegalArgumentException(
- "Location object not complete. Missing timestamps or accuracy?");
+ // really throw it!
+ throw e;
}
}
try {
mService.setTestProviderLocation(provider, loc);
- } catch (RemoteException ex) {
- Log.e(TAG, "setTestProviderLocation: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1265,12 +1381,14 @@ public class LocationManager {
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void clearTestProviderLocation(String provider) {
try {
mService.clearTestProviderLocation(provider);
- } catch (RemoteException ex) {
- Log.e(TAG, "clearTestProviderLocation: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1285,12 +1403,14 @@ public class LocationManager {
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void setTestProviderEnabled(String provider, boolean enabled) {
try {
mService.setTestProviderEnabled(provider, enabled);
- } catch (RemoteException ex) {
- Log.e(TAG, "setTestProviderEnabled: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1303,14 +1423,15 @@ public class LocationManager {
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void clearTestProviderEnabled(String provider) {
try {
mService.clearTestProviderEnabled(provider);
- } catch (RemoteException ex) {
- Log.e(TAG, "clearTestProviderEnabled: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
-
}
/**
@@ -1326,12 +1447,14 @@ public class LocationManager {
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
try {
mService.setTestProviderStatus(provider, status, extras, updateTime);
- } catch (RemoteException ex) {
- Log.e(TAG, "setTestProviderStatus: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1344,12 +1467,14 @@ public class LocationManager {
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public void clearTestProviderStatus(String provider) {
try {
mService.clearTestProviderStatus(provider);
- } catch (RemoteException ex) {
- Log.e(TAG, "clearTestProviderStatus: RemoteException", ex);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
}
}
@@ -1587,7 +1712,9 @@ public class LocationManager {
* The provider may optionally fill the extras Bundle with results from the command.
*
* @return true if the command succeeds.
+ * @deprecated use the {@link LocationRequest} class instead
*/
+ @Deprecated
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
try {
return mService.sendExtraCommand(provider, command, extras);
@@ -1612,4 +1739,41 @@ public class LocationManager {
}
}
+ private static void checkProvider(String provider) {
+ if (provider == null) {
+ throw new IllegalArgumentException("invalid provider: " + provider);
+ }
+ }
+
+ private static void checkCriteria(Criteria criteria) {
+ if (criteria == null) {
+ throw new IllegalArgumentException("invalid criteria: " + criteria);
+ }
+ }
+ private static void checkListener(LocationListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("invalid listener: " + listener);
+ }
+ }
+
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + intent);
+ }
+ if (!intent.isTargetedToPackage()) {
+ IllegalArgumentException e = new IllegalArgumentException(
+ "pending intent msut be targeted to package");
+ if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
+ throw e;
+ } else {
+ Log.w(TAG, e);
+ }
+ }
+ }
+
+ private static void checkGeofence(Geofence fence) {
+ if (fence == null) {
+ throw new IllegalArgumentException("invalid geofence: " + fence);
+ }
+ }
}
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index 8c16580..737e17f 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -16,8 +16,8 @@
package android.location;
-import android.os.RemoteException;
-import android.util.Log;
+
+import com.android.internal.location.ProviderProperties;
/**
* An abstract superclass for location providers. A location provider
@@ -32,35 +32,40 @@ import android.util.Log;
* characteristics or monetary costs to the user. The {@link
* Criteria} class allows providers to be selected based on
* user-specified criteria.
+ *
+ * @deprecated Use the {@link Criteria} class to request location instead of
+ * enumerating providers.
*/
-public abstract class LocationProvider {
- private static final String TAG = "LocationProvider";
- // A regular expression matching characters that may not appear
- // in the name of a LocationProvider.
- static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
-
- private final String mName;
- private final ILocationManager mService;
-
+@Deprecated
+public class LocationProvider {
public static final int OUT_OF_SERVICE = 0;
public static final int TEMPORARILY_UNAVAILABLE = 1;
public static final int AVAILABLE = 2;
/**
+ * A regular expression matching characters that may not appear
+ * in the name of a LocationProvider
+ * @hide
+ */
+ public static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
+
+ private final String mName;
+ private final ProviderProperties mProperties;
+
+ /**
* Constructs a LocationProvider with the given name. Provider names must
* consist only of the characters [a-zA-Z0-9].
*
* @throws IllegalArgumentException if name contains an illegal character
*
- * {@hide}
+ * @hide
*/
- public LocationProvider(String name, ILocationManager service) {
+ public LocationProvider(String name, ProviderProperties properties) {
if (name.matches(BAD_CHARS_REGEX)) {
- throw new IllegalArgumentException("name " + name +
- " contains an illegal character");
+ throw new IllegalArgumentException("provider name contains illegal character: " + name);
}
mName = name;
- mService = service;
+ mProperties = properties;
}
/**
@@ -75,40 +80,81 @@ public abstract class LocationProvider {
* false otherwise.
*/
public boolean meetsCriteria(Criteria criteria) {
- try {
- return mService.providerMeetsCriteria(mName, criteria);
- } catch (RemoteException e) {
- Log.e(TAG, "meetsCriteria: RemoteException", e);
+ return propertiesMeetCriteria(mName, mProperties, criteria);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean propertiesMeetCriteria(String name, ProviderProperties properties,
+ Criteria criteria) {
+ if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
+ // passive provider never matches
+ return false;
+ }
+ if (properties == null) {
+ // unfortunately this can happen for provider in remote services
+ // that have not finished binding yet
+ return false;
+ }
+
+ if (criteria.getAccuracy() != Criteria.NO_REQUIREMENT &&
+ criteria.getAccuracy() < properties.mAccuracy) {
return false;
}
+ if (criteria.getPowerRequirement() != Criteria.NO_REQUIREMENT &&
+ criteria.getPowerRequirement() < properties.mPowerRequirement) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !properties.mSupportsAltitude) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !properties.mSupportsSpeed) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !properties.mSupportsBearing) {
+ return false;
+ }
+ if (!criteria.isCostAllowed() && properties.mHasMonetaryCost) {
+ return false;
+ }
+ return true;
}
/**
* Returns true if the provider requires access to a
* data network (e.g., the Internet), false otherwise.
*/
- public abstract boolean requiresNetwork();
+ public boolean requiresNetwork() {
+ return mProperties.mRequiresNetwork;
+ }
/**
* Returns true if the provider requires access to a
* satellite-based positioning system (e.g., GPS), false
* otherwise.
*/
- public abstract boolean requiresSatellite();
+ public boolean requiresSatellite() {
+ return mProperties.mRequiresSatellite;
+ }
/**
* Returns true if the provider requires access to an appropriate
* cellular network (e.g., to make use of cell tower IDs), false
* otherwise.
*/
- public abstract boolean requiresCell();
+ public boolean requiresCell() {
+ return mProperties.mRequiresCell;
+ }
/**
* Returns true if the use of this provider may result in a
* monetary charge to the user, false if use is free. It is up to
* each provider to give accurate information.
*/
- public abstract boolean hasMonetaryCost();
+ public boolean hasMonetaryCost() {
+ return mProperties.mHasMonetaryCost;
+ }
/**
* Returns true if the provider is able to provide altitude
@@ -116,7 +162,9 @@ public abstract class LocationProvider {
* under most circumstances but may occassionally not report it
* should return true.
*/
- public abstract boolean supportsAltitude();
+ public boolean supportsAltitude() {
+ return mProperties.mSupportsAltitude;
+ }
/**
* Returns true if the provider is able to provide speed
@@ -124,7 +172,9 @@ public abstract class LocationProvider {
* under most circumstances but may occassionally not report it
* should return true.
*/
- public abstract boolean supportsSpeed();
+ public boolean supportsSpeed() {
+ return mProperties.mSupportsSpeed;
+ }
/**
* Returns true if the provider is able to provide bearing
@@ -132,7 +182,9 @@ public abstract class LocationProvider {
* under most circumstances but may occassionally not report it
* should return true.
*/
- public abstract boolean supportsBearing();
+ public boolean supportsBearing() {
+ return mProperties.mSupportsBearing;
+ }
/**
* Returns the power requirement for this provider.
@@ -140,7 +192,9 @@ public abstract class LocationProvider {
* @return the power requirement for this provider, as one of the
* constants Criteria.POWER_REQUIREMENT_*.
*/
- public abstract int getPowerRequirement();
+ public int getPowerRequirement() {
+ return mProperties.mPowerRequirement;
+ }
/**
* Returns a constant describing horizontal accuracy of this provider.
@@ -149,5 +203,7 @@ public abstract class LocationProvider {
* location is only approximate then {@link Criteria#ACCURACY_COARSE}
* is returned.
*/
- public abstract int getAccuracy();
+ public int getAccuracy() {
+ return mProperties.mAccuracy;
+ }
}
diff --git a/location/java/android/location/LocationRequest.aidl b/location/java/android/location/LocationRequest.aidl
new file mode 100644
index 0000000..b1a8647
--- /dev/null
+++ b/location/java/android/location/LocationRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable LocationRequest;
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
new file mode 100644
index 0000000..3110196
--- /dev/null
+++ b/location/java/android/location/LocationRequest.java
@@ -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();
+ }
+}
diff --git a/location/java/com/android/internal/location/DummyLocationProvider.java b/location/java/com/android/internal/location/DummyLocationProvider.java
deleted file mode 100644
index 3122960..0000000
--- a/location/java/com/android/internal/location/DummyLocationProvider.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2007 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.internal.location;
-
-import android.location.ILocationManager;
-import android.location.LocationProvider;
-
-/**
- * A stub implementation of LocationProvider used by LocationManager.
- * A DummyLocationProvider may be queried to determine the properties
- * of the provider whcih it shadows, but does not actually provide location
- * data.
- *
- * {@hide}
- */
-public class DummyLocationProvider extends LocationProvider {
-
- private static final String TAG = "DummyLocationProvider";
-
- String mName;
- boolean mRequiresNetwork;
- boolean mRequiresSatellite;
- boolean mRequiresCell;
- boolean mHasMonetaryCost;
- boolean mSupportsAltitude;
- boolean mSupportsSpeed;
- boolean mSupportsBearing;
- int mPowerRequirement;
- int mAccuracy;
-
- public DummyLocationProvider(String name, ILocationManager service) {
- super(name, service);
- }
-
- public void setRequiresNetwork(boolean requiresNetwork) {
- mRequiresNetwork = requiresNetwork;
- }
-
- public void setRequiresSatellite(boolean requiresSatellite) {
- mRequiresSatellite = requiresSatellite;
- }
-
- public void setRequiresCell(boolean requiresCell) {
- mRequiresCell = requiresCell;
- }
-
- public void setHasMonetaryCost(boolean hasMonetaryCost) {
- mHasMonetaryCost = hasMonetaryCost;
- }
-
- public void setSupportsAltitude(boolean supportsAltitude) {
- mSupportsAltitude = supportsAltitude;
- }
-
- public void setSupportsSpeed(boolean supportsSpeed) {
- mSupportsSpeed = supportsSpeed;
- }
-
- public void setSupportsBearing(boolean supportsBearing) {
- mSupportsBearing = supportsBearing;
- }
-
- public void setPowerRequirement(int powerRequirement) {
- mPowerRequirement = powerRequirement;
- }
-
- public void setAccuracy(int accuracy) {
- mAccuracy = accuracy;
- }
-
- /**
- * Returns true if the provider requires access to a
- * data network (e.g., the Internet), false otherwise.
- */
- @Override
- public boolean requiresNetwork() {
- return mRequiresNetwork;
- }
-
- /**
- * Returns true if the provider requires access to a
- * satellite-based positioning system (e.g., GPS), false
- * otherwise.
- */
- @Override
- public boolean requiresSatellite() {
- return mRequiresSatellite;
- }
-
- /**
- * Returns true if the provider requires access to an appropriate
- * cellular network (e.g., to make use of cell tower IDs), false
- * otherwise.
- */
- @Override
- public boolean requiresCell() {
- return mRequiresCell;
- }
-
- /**
- * Returns true if the use of this provider may result in a
- * monetary charge to the user, false if use is free. It is up to
- * each provider to give accurate information.
- */
- @Override
- public boolean hasMonetaryCost() {
- return mHasMonetaryCost;
- }
-
- /**
- * Returns true if the provider is able to provide altitude
- * information, false otherwise. A provider that reports altitude
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- @Override
- public boolean supportsAltitude() {
- return mSupportsAltitude;
- }
-
- /**
- * Returns true if the provider is able to provide speed
- * information, false otherwise. A provider that reports speed
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- @Override
- public boolean supportsSpeed() {
- return mSupportsSpeed;
- }
-
- /**
- * Returns true if the provider is able to provide bearing
- * information, false otherwise. A provider that reports bearing
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- @Override
- public boolean supportsBearing() {
- return mSupportsBearing;
- }
-
- /**
- * Returns the power requirement for this provider.
- *
- * @return the power requirement for this provider, as one of the
- * constants Criteria.POWER_REQUIREMENT_*.
- */
- @Override
- public int getPowerRequirement() {
- return mPowerRequirement;
- }
-
- /**
- * Returns a constant describing the horizontal accuracy returned
- * by this provider.
- *
- * @return the horizontal accuracy for this provider, as one of the
- * constants Criteria.ACCURACY_*.
- */
- @Override
- public int getAccuracy() {
- return mAccuracy;
- }
-}
-
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index ecf6789..39c2d92 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -14,40 +14,31 @@
* limitations under the License.
*/
-package android.location;
+package com.android.internal.location;
-import android.location.Criteria;
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.WorkSource;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
/**
* Binder interface for services that implement location providers.
- *
- * {@hide}
+ * <p>Use {@link LocationProviderBase} as a helper to implement this
+ * interface.
+ * @hide
*/
interface ILocationProvider {
- boolean requiresNetwork();
- boolean requiresSatellite();
- boolean requiresCell();
- boolean hasMonetaryCost();
- boolean supportsAltitude();
- boolean supportsSpeed();
- boolean supportsBearing();
- int getPowerRequirement();
- boolean meetsCriteria(in Criteria criteria);
- int getAccuracy();
void enable();
void disable();
+
+ void setRequest(in ProviderRequest request, in WorkSource ws);
+
+ // --- deprecated (but still supported) ---
+ ProviderProperties getProperties();
int getStatus(out Bundle extras);
long getStatusUpdateTime();
- String getInternalState();
- void enableLocationTracking(boolean enable);
- void setMinTime(long minTime, in WorkSource ws);
- void updateNetworkState(int state, in NetworkInfo info);
- void updateLocation(in Location location);
boolean sendExtraCommand(String command, inout Bundle extras);
- void addListener(int uid);
- void removeListener(int uid);
}
diff --git a/location/java/com/android/internal/location/ProviderProperties.aidl b/location/java/com/android/internal/location/ProviderProperties.aidl
new file mode 100644
index 0000000..b901444
--- /dev/null
+++ b/location/java/com/android/internal/location/ProviderProperties.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.internal.location;
+
+parcelable ProviderProperties;
diff --git a/location/java/com/android/internal/location/ProviderProperties.java b/location/java/com/android/internal/location/ProviderProperties.java
new file mode 100644
index 0000000..08aed80
--- /dev/null
+++ b/location/java/com/android/internal/location/ProviderProperties.java
@@ -0,0 +1,152 @@
+/*
+ * 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.internal.location;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A Parcelable containing (legacy) location provider properties.
+ * This object is just used inside the framework and system services.
+ * @hide
+ */
+public final class ProviderProperties implements Parcelable {
+ /**
+ * True if provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ public final boolean mRequiresNetwork;
+
+ /**
+ * True if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ public final boolean mRequiresSatellite;
+
+ /**
+ * True if the provider requires access to an appropriate
+ * cellular network (e.g., to make use of cell tower IDs), false
+ * otherwise.
+ */
+ public final boolean mRequiresCell;
+
+ /**
+ * True if the use of this provider may result in a
+ * monetary charge to the user, false if use is free. It is up to
+ * each provider to give accurate information. Cell (network) usage
+ * is not considered monetary cost.
+ */
+ public final boolean mHasMonetaryCost;
+
+ /**
+ * True if the provider is able to provide altitude
+ * information, false otherwise. A provider that reports altitude
+ * under most circumstances but may occasionally not report it
+ * should return true.
+ */
+ public final boolean mSupportsAltitude;
+
+ /**
+ * True if the provider is able to provide speed
+ * information, false otherwise. A provider that reports speed
+ * under most circumstances but may occasionally not report it
+ * should return true.
+ */
+ public final boolean mSupportsSpeed;
+
+ /**
+ * True if the provider is able to provide bearing
+ * information, false otherwise. A provider that reports bearing
+ * under most circumstances but may occasionally not report it
+ * should return true.
+ */
+ public final boolean mSupportsBearing;
+
+ /**
+ * Power requirement for this provider.
+ *
+ * @return the power requirement for this provider, as one of the
+ * constants Criteria.POWER_*.
+ */
+ public final int mPowerRequirement;
+
+ /**
+ * Constant describing the horizontal accuracy returned
+ * by this provider.
+ *
+ * @return the horizontal accuracy for this provider, as one of the
+ * constants Criteria.ACCURACY_COARSE or Criteria.ACCURACY_FINE
+ */
+ public final int mAccuracy;
+
+ public ProviderProperties(boolean mRequiresNetwork,
+ boolean mRequiresSatellite, boolean mRequiresCell, boolean mHasMonetaryCost,
+ boolean mSupportsAltitude, boolean mSupportsSpeed, boolean mSupportsBearing,
+ int mPowerRequirement, int mAccuracy) {
+ this.mRequiresNetwork = mRequiresNetwork;
+ this.mRequiresSatellite = mRequiresSatellite;
+ this.mRequiresCell = mRequiresCell;
+ this.mHasMonetaryCost = mHasMonetaryCost;
+ this.mSupportsAltitude = mSupportsAltitude;
+ this.mSupportsSpeed = mSupportsSpeed;
+ this.mSupportsBearing = mSupportsBearing;
+ this.mPowerRequirement = mPowerRequirement;
+ this.mAccuracy = mAccuracy;
+ }
+
+ public static final Parcelable.Creator<ProviderProperties> CREATOR =
+ new Parcelable.Creator<ProviderProperties>() {
+ @Override
+ public ProviderProperties createFromParcel(Parcel in) {
+ boolean requiresNetwork = in.readInt() == 1;
+ boolean requiresSatellite = in.readInt() == 1;
+ boolean requiresCell = in.readInt() == 1;
+ boolean hasMonetaryCost = in.readInt() == 1;
+ boolean supportsAltitude = in.readInt() == 1;
+ boolean supportsSpeed = in.readInt() == 1;
+ boolean supportsBearing = in.readInt() == 1;
+ int powerRequirement = in.readInt();
+ int accuracy = in.readInt();
+ return new ProviderProperties(requiresNetwork, requiresSatellite,
+ requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing,
+ powerRequirement, accuracy);
+ }
+ @Override
+ public ProviderProperties[] newArray(int size) {
+ return new ProviderProperties[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mRequiresNetwork ? 1 : 0);
+ parcel.writeInt(mRequiresSatellite ? 1 : 0);
+ parcel.writeInt(mRequiresCell ? 1 : 0);
+ parcel.writeInt(mHasMonetaryCost ? 1 : 0);
+ parcel.writeInt(mSupportsAltitude ? 1 : 0);
+ parcel.writeInt(mSupportsSpeed ? 1 : 0);
+ parcel.writeInt(mSupportsSpeed ? 1 : 0);
+ parcel.writeInt(mPowerRequirement);
+ parcel.writeInt(mAccuracy);
+ }
+}
diff --git a/location/java/com/android/internal/location/ProviderRequest.aidl b/location/java/com/android/internal/location/ProviderRequest.aidl
new file mode 100644
index 0000000..4e1ea95
--- /dev/null
+++ b/location/java/com/android/internal/location/ProviderRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.internal.location;
+
+parcelable ProviderRequest;
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
new file mode 100644
index 0000000..25c51f5
--- /dev/null
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.internal.location;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.location.LocationRequest;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.TimeUtils;
+
+/** @hide */
+public final class ProviderRequest implements Parcelable {
+ /** Location reporting is requested (true) */
+ public boolean reportLocation = false;
+
+ /** The smallest requested interval */
+ public long interval = Long.MAX_VALUE;
+
+ /**
+ * A more detailed set of requests.
+ * <p>Location Providers can optionally use this to
+ * fine tune location updates, for example when there
+ * is a high power slow interval request and a
+ * low power fast interval request.
+ */
+ public List<LocationRequest> locationRequests = null;
+
+ public ProviderRequest() {
+ }
+
+ public static final Parcelable.Creator<ProviderRequest> CREATOR =
+ new Parcelable.Creator<ProviderRequest>() {
+ @Override
+ public ProviderRequest createFromParcel(Parcel in) {
+ ProviderRequest request = new ProviderRequest();
+ request.reportLocation = in.readInt() == 1;
+ request.interval = in.readLong();
+ int count = in.readInt();
+ request.locationRequests = new ArrayList<LocationRequest>(count);
+ for (int i = 0; i < count; i++) {
+ request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
+ }
+ return request;
+ }
+ @Override
+ public ProviderRequest[] newArray(int size) {
+ return new ProviderRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(reportLocation ? 1 : 0);
+ parcel.writeLong(interval);
+ parcel.writeParcelableArray(locationRequests.toArray(
+ new LocationRequest[locationRequests.size()]), 0);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("ProviderRequest[");
+ if (reportLocation) {
+ s.append("ON");
+ s.append(" interval=");
+ TimeUtils.formatDuration(interval, s);
+ } else {
+ s.append("OFF");
+ }
+ s.append(']');
+ return s.toString();
+ }
+}
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
index a06478a..62f5677 100644
--- a/location/lib/Android.mk
+++ b/location/lib/Android.mk
@@ -23,7 +23,8 @@ LOCAL_MODULE:= com.android.location.provider
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
- $(call all-subdir-java-files)
+ $(call all-subdir-java-files) \
+ $(call all-aidl-files-under, java)
include $(BUILD_JAVA_LIBRARY)
diff --git a/location/lib/java/com/android/location/provider/LocationProvider.java b/location/lib/java/com/android/location/provider/LocationProvider.java
deleted file mode 100644
index 3714f40..0000000
--- a/location/lib/java/com/android/location/provider/LocationProvider.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2010 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.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;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.WorkSource;
-import android.util.Log;
-
-/**
- * An abstract superclass for location providers that are implemented
- * outside of the core android platform.
- * Location providers can be implemented as services and return the result of
- * {@link LocationProvider#getBinder()} in its getBinder() method.
- *
- * @hide
- */
-public abstract class LocationProvider {
-
- private static final String TAG = "LocationProvider";
-
- private ILocationManager mLocationManager;
-
- private ILocationProvider.Stub mProvider = new ILocationProvider.Stub() {
-
- public boolean requiresNetwork() {
- return LocationProvider.this.onRequiresNetwork();
- }
-
- public boolean requiresSatellite() {
- return LocationProvider.this.onRequiresSatellite();
- }
-
- public boolean requiresCell() {
- return LocationProvider.this.onRequiresCell();
- }
-
- public boolean hasMonetaryCost() {
- return LocationProvider.this.onHasMonetaryCost();
- }
-
- public boolean supportsAltitude() {
- return LocationProvider.this.onSupportsAltitude();
- }
-
- public boolean supportsSpeed() {
- return LocationProvider.this.onSupportsSpeed();
- }
-
- public boolean supportsBearing() {
- return LocationProvider.this.onSupportsBearing();
- }
-
- public int getPowerRequirement() {
- return LocationProvider.this.onGetPowerRequirement();
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- return LocationProvider.this.onMeetsCriteria(criteria);
- }
-
- public int getAccuracy() {
- return LocationProvider.this.onGetAccuracy();
- }
-
- public void enable() {
- LocationProvider.this.onEnable();
- }
-
- public void disable() {
- LocationProvider.this.onDisable();
- }
-
- public int getStatus(Bundle extras) {
- return LocationProvider.this.onGetStatus(extras);
- }
-
- public long getStatusUpdateTime() {
- return LocationProvider.this.onGetStatusUpdateTime();
- }
-
- public String getInternalState() {
- return LocationProvider.this.onGetInternalState();
- }
-
- public void enableLocationTracking(boolean enable) {
- LocationProvider.this.onEnableLocationTracking(enable);
- }
-
- public void setMinTime(long minTime, WorkSource ws) {
- LocationProvider.this.onSetMinTime(minTime, ws);
- }
-
- public void updateNetworkState(int state, NetworkInfo info) {
- LocationProvider.this.onUpdateNetworkState(state, info);
- }
-
- public void updateLocation(Location location) {
- LocationProvider.this.onUpdateLocation(location);
- }
-
- public boolean sendExtraCommand(String command, Bundle extras) {
- return LocationProvider.this.onSendExtraCommand(command, extras);
- }
-
- public void addListener(int uid) {
- LocationProvider.this.onAddListener(uid, new WorkSource(uid));
- }
-
- public void removeListener(int uid) {
- LocationProvider.this.onRemoveListener(uid, new WorkSource(uid));
- }
- };
-
- public LocationProvider() {
- IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
- mLocationManager = ILocationManager.Stub.asInterface(b);
- }
-
- /**
- * {@hide}
- */
- /* package */ ILocationProvider getInterface() {
- return mProvider;
- }
-
- /**
- * Returns the Binder interface for the location provider.
- * This is intended to be used for the onBind() method of
- * a service that implements a location provider service.
- *
- * @return the IBinder instance for the provider
- */
- public IBinder getBinder() {
- return mProvider;
- }
-
- /**
- * Used by the location provider to report new locations.
- *
- * @param location new Location to report
- *
- * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
- */
- public void reportLocation(Location location) {
- try {
- mLocationManager.reportLocation(location, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in reportLocation: ", e);
- }
- }
-
- /**
- * Returns true if the provider requires access to a
- * data network (e.g., the Internet), false otherwise.
- */
- public abstract boolean onRequiresNetwork();
-
- /**
- * Returns true if the provider requires access to a
- * satellite-based positioning system (e.g., GPS), false
- * otherwise.
- */
- public abstract boolean onRequiresSatellite();
-
- /**
- * Returns true if the provider requires access to an appropriate
- * cellular network (e.g., to make use of cell tower IDs), false
- * otherwise.
- */
- public abstract boolean onRequiresCell();
-
- /**
- * Returns true if the use of this provider may result in a
- * monetary charge to the user, false if use is free. It is up to
- * each provider to give accurate information.
- */
- public abstract boolean onHasMonetaryCost();
-
- /**
- * Returns true if the provider is able to provide altitude
- * information, false otherwise. A provider that reports altitude
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public abstract boolean onSupportsAltitude();
-
- /**
- * Returns true if the provider is able to provide speed
- * information, false otherwise. A provider that reports speed
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public abstract boolean onSupportsSpeed();
-
- /**
- * Returns true if the provider is able to provide bearing
- * information, false otherwise. A provider that reports bearing
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public abstract boolean onSupportsBearing();
-
- /**
- * Returns the power requirement for this provider.
- *
- * @return the power requirement for this provider, as one of the
- * constants Criteria.POWER_REQUIREMENT_*.
- */
- 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,
- * {@link Criteria#ACCURACY_FINE} is returned, otherwise if the
- * location is only approximate then {@link Criteria#ACCURACY_COARSE}
- * is returned.
- */
- public abstract int onGetAccuracy();
-
- /**
- * Enables the location provider
- */
- public abstract void onEnable();
-
- /**
- * Disables the location provider
- */
- public abstract void onDisable();
-
- /**
- * Returns a information on the status of this provider.
- * {@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
- * out of service, and this is not expected to change in the near
- * future; {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} is returned if
- * the provider is temporarily unavailable but is expected to be
- * available shortly; and {@link android.location.LocationProvider#AVAILABLE} is returned
- * if the provider is currently available.
- *
- * <p> If extras is non-null, additional status information may be
- * added to it in the form of provider-specific key/value pairs.
- */
- public abstract int onGetStatus(Bundle extras);
-
- /**
- * Returns the time at which the status was last updated. It is the
- * responsibility of the provider to appropriately set this value using
- * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
- * there is a status update that it wishes to broadcast to all its
- * listeners. The provider should be careful not to broadcast
- * the same status again.
- *
- * @return time of last status update in millis since last reboot
- */
- public abstract long onGetStatusUpdateTime();
-
- /**
- * Returns debugging information about the location provider.
- *
- * @return string describing the internal state of the location provider, or null.
- */
- public abstract String onGetInternalState();
-
- /**
- * Notifies the location provider that clients are listening for locations.
- * Called with enable set to true when the first client is added and
- * called with enable set to false when the last client is removed.
- * This allows the provider to prepare for receiving locations,
- * and to shut down when no clients are remaining.
- *
- * @param enable true if location tracking should be enabled.
- */
- public abstract void onEnableLocationTracking(boolean enable);
-
- /**
- * Notifies the location provider of the smallest minimum time between updates amongst
- * all clients that are listening for locations. This allows the provider to reduce
- * the frequency of updates to match the requested frequency.
- *
- * @param minTime the smallest minTime value over all listeners for this provider.
- * @param ws the source this work is coming from.
- */
- public abstract void onSetMinTime(long minTime, WorkSource ws);
-
- /**
- * Updates the network state for the given provider. This function must
- * be overwritten if {@link android.location.LocationProvider#requiresNetwork} returns true.
- * The state is {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} (disconnected)
- * OR {@link android.location.LocationProvider#AVAILABLE} (connected or connecting).
- *
- * @param state data state
- */
- public abstract void onUpdateNetworkState(int state, NetworkInfo info);
-
- /**
- * Informs the provider when a new location has been computed by a different
- * location provider. This is intended to be used as aiding data for the
- * receiving provider.
- *
- * @param location new location from other location provider
- */
- public abstract void onUpdateLocation(Location location);
-
- /**
- * Implements addditional location provider specific additional commands.
- *
- * @param command name of the command to send to the provider.
- * @param extras optional arguments for the command (or null).
- * The provider may optionally fill the extras Bundle with results from the command.
- *
- * @return true if the command succeeds.
- */
- public abstract boolean onSendExtraCommand(String command, Bundle extras);
-
- /**
- * Notifies the location provider when a new client is listening for locations.
- *
- * @param uid user ID of the new client.
- * @param ws a WorkSource representation of the client.
- */
- public abstract void onAddListener(int uid, WorkSource ws);
-
- /**
- * Notifies the location provider when a client is no longer listening for locations.
- *
- * @param uid user ID of the client no longer listening.
- * @param ws a WorkSource representation of the client.
- */
- public abstract void onRemoveListener(int uid, WorkSource ws);
-}
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
new file mode 100644
index 0000000..53b0cae
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2010 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.provider;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+
+import android.content.Context;
+import android.location.ILocationManager;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.WorkSource;
+import android.util.Log;
+
+import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+
+/**
+ * Base class for location providers implemented as services.
+ * @hide
+ */
+public abstract class LocationProviderBase {
+ private final String TAG;
+
+ protected final ILocationManager mLocationManager;
+ private final ProviderProperties mProperties;
+ private final IBinder mBinder;
+
+ private final class Service extends ILocationProvider.Stub {
+ @Override
+ public void enable() {
+ onEnable();
+ }
+ @Override
+ public void disable() {
+ onDisable();
+ }
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource ws) {
+ onSetRequest(new ProviderRequestUnbundled(request), ws);
+ }
+ @Override
+ public ProviderProperties getProperties() {
+ return mProperties;
+ }
+ @Override
+ public int getStatus(Bundle extras) {
+ return onGetStatus(extras);
+ }
+ @Override
+ public long getStatusUpdateTime() {
+ return onGetStatusUpdateTime();
+ }
+ @Override
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return onSendExtraCommand(command, extras);
+ }
+ @Override
+ public void dump(FileDescriptor fd, String[] args) {
+ PrintWriter pw = new PrintWriter(new FileOutputStream(fd));
+ onDump(fd, pw, args);
+ pw.flush();
+ }
+ }
+
+ public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
+ TAG = tag;
+ IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
+ mLocationManager = ILocationManager.Stub.asInterface(b);
+ mProperties = properties.getProviderProperties();
+ mBinder = new Service();
+ }
+
+ public IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Used by the location provider to report new locations.
+ *
+ * @param location new Location to report
+ *
+ * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+ */
+ public final void reportLocation(Location location) {
+ try {
+ mLocationManager.reportLocation(location, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
+ } catch (Exception e) {
+ // never crash provider, might be running in a system process
+ Log.e(TAG, "Exception", e);
+ }
+ }
+
+ /**
+ * Enable the location provider.
+ * <p>The provider may initialize resources, but does
+ * not yet need to report locations.
+ */
+ public abstract void onEnable();
+
+ /**
+ * Disable the location provider.
+ * <p>The provider must release resources, and stop
+ * performing work. It may no longer report locations.
+ */
+ public abstract void onDisable();
+
+ /**
+ * Set the {@link ProviderRequest} requirements for this provider.
+ * <p>Each call to this method overrides all previous requests.
+ * <p>This method might trigger the provider to start returning
+ * locations, or to stop returning locations, depending on the
+ * parameters in the request.
+ */
+ public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
+
+ /**
+ * Dump debug information.
+ */
+ public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ }
+
+ /**
+ * Returns a information on the status of this provider.
+ * <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
+ * out of service, and this is not expected to change in the near
+ * future; {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} is returned if
+ * the provider is temporarily unavailable but is expected to be
+ * available shortly; and {@link android.location.LocationProvider#AVAILABLE} is returned
+ * if the provider is currently available.
+ *
+ * <p>If extras is non-null, additional status information may be
+ * added to it in the form of provider-specific key/value pairs.
+ */
+ public abstract int onGetStatus(Bundle extras);
+
+ /**
+ * Returns the time at which the status was last updated. It is the
+ * responsibility of the provider to appropriately set this value using
+ * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
+ * there is a status update that it wishes to broadcast to all its
+ * listeners. The provider should be careful not to broadcast
+ * the same status again.
+ *
+ * @return time of last status update in millis since last reboot
+ */
+ public abstract long onGetStatusUpdateTime();
+
+ /**
+ * Implements addditional location provider specific additional commands.
+ *
+ * @param command name of the command to send to the provider.
+ * @param extras optional arguments for the command (or null).
+ * The provider may optionally fill the extras Bundle with results from the command.
+ *
+ * @return true if the command succeeds.
+ */
+ public boolean onSendExtraCommand(String command, Bundle extras) {
+ // default implementation
+ return false;
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
new file mode 100644
index 0000000..c8bdda4
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.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.provider;
+
+import com.android.internal.location.ProviderProperties;
+
+/**
+ * This class is a public API for unbundled providers,
+ * that hides the (hidden framework) ProviderProperties.
+ * <p>Do _not_ remove public methods on this class.
+ */
+public final class ProviderPropertiesUnbundled {
+ private final ProviderProperties mProperties;
+
+ public static ProviderPropertiesUnbundled create(boolean requiresNetwork,
+ boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost,
+ boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing,
+ int powerRequirement, int accuracy) {
+ return new ProviderPropertiesUnbundled(new ProviderProperties(requiresNetwork,
+ requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
+ supportsBearing, powerRequirement, accuracy));
+ }
+
+ private ProviderPropertiesUnbundled(ProviderProperties properties) {
+ mProperties = properties;
+ }
+
+ public ProviderProperties getProviderProperties() {
+ return mProperties;
+ }
+
+ @Override
+ public String toString() {
+ return mProperties.toString();
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
new file mode 100644
index 0000000..7487a56
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -0,0 +1,53 @@
+/*
+ * 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.provider;
+
+import java.util.List;
+
+import android.location.LocationRequest;
+
+import com.android.internal.location.ProviderRequest;
+
+/**
+ * This class is a public API for unbundled providers,
+ * that hides the (hidden framework) ProviderRequest.
+ * <p>Do _not_ remove public methods on this class.
+ */
+public final class ProviderRequestUnbundled {
+ private final ProviderRequest mRequest;
+
+ public ProviderRequestUnbundled(ProviderRequest request) {
+ mRequest = request;
+ }
+
+ public boolean getReportLocation() {
+ return mRequest.reportLocation;
+ }
+
+ public long getInterval() {
+ return mRequest.interval;
+ }
+
+ public List<LocationRequest> getLocationRequests() {
+ return mRequest.locationRequests;
+ }
+
+ @Override
+ public String toString() {
+ return mRequest.toString();
+ }
+}
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);
+ }
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 198ba8b..a6c3860 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,21 +16,21 @@
package com.android.server;
-import android.app.Activity;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
+import android.location.Geofence;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
@@ -39,26 +39,28 @@ import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
+import android.location.LocationRequest;
import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcelable;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.WorkSource;
import android.provider.Settings;
import android.provider.Settings.NameValueTable;
import android.util.Log;
import android.util.Slog;
-import android.util.PrintWriterPrinter;
import com.android.internal.content.PackageMonitor;
-
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
@@ -70,8 +72,7 @@ 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.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -83,135 +84,273 @@ import java.util.Set;
/**
* The service class that manages LocationProviders and issues location
* updates and alerts.
- *
- * {@hide}
*/
-public class LocationManagerService extends ILocationManager.Stub implements Runnable {
+public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
private static final String TAG = "LocationManagerService";
- private static final boolean LOCAL_LOGV = false;
+ public static final boolean D = false;
+
+ private static final String WAKELOCK_KEY = TAG;
+ private static final String THREAD_NAME = TAG;
private static final String ACCESS_FINE_LOCATION =
- android.Manifest.permission.ACCESS_FINE_LOCATION;
+ android.Manifest.permission.ACCESS_FINE_LOCATION;
private static final String ACCESS_COARSE_LOCATION =
- android.Manifest.permission.ACCESS_COARSE_LOCATION;
+ android.Manifest.permission.ACCESS_COARSE_LOCATION;
private static final String ACCESS_MOCK_LOCATION =
- android.Manifest.permission.ACCESS_MOCK_LOCATION;
+ android.Manifest.permission.ACCESS_MOCK_LOCATION;
private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
- android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
+ android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
private static final String INSTALL_LOCATION_PROVIDER =
- android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+ android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+
+ private static final String NETWORK_LOCATION_SERVICE_ACTION =
+ "com.android.location.service.v2.NetworkLocationProvider";
+ private static final String FUSED_LOCATION_SERVICE_ACTION =
+ "com.android.location.service.FusedLocationProvider";
+
+ private static final int MSG_LOCATION_CHANGED = 1;
+
+ // Accuracy in meters above which a location is considered coarse
+ private static final double COARSE_ACCURACY_M = 100.0;
+ private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
+
+ private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
+
+ /**
+ * Maximum latitude of 1 meter from the pole.
+ * This keeps cosine(MAX_LATITUDE) to a non-zero value;
+ */
+ private static final double MAX_LATITUDE =
+ 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
// Location Providers may sometimes deliver location updates
// slightly faster that requested - provide grace period so
// we don't unnecessarily filter events that are otherwise on
// time
- private static final int MAX_PROVIDER_SCHEDULING_JITTER = 100;
+ private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- // Set of providers that are explicitly enabled
- private final Set<String> mEnabledProviders = new HashSet<String>();
+ private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
- // Set of providers that are explicitly disabled
- private final Set<String> mDisabledProviders = new HashSet<String>();
-
- // Locations, status values, and extras for mock providers
- private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
+ private final Context mContext;
- private static boolean sProvidersLoaded = false;
+ // used internally for synchronization
+ private final Object mLock = new Object();
- private final Context mContext;
- private PackageManager mPackageManager; // final after initialize()
- private String mNetworkLocationProviderPackageName; // only used on handler thread
- private String mGeocodeProviderPackageName; // only used on handler thread
+ // --- fields below are final after init() ---
+ private GeofenceManager mGeofenceManager;
+ private PowerManager.WakeLock mWakeLock;
+ private PackageManager mPackageManager;
private GeocoderProxy mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
+ // track the passive provider for some special cases
+ private PassiveProvider mPassiveProvider;
- // Cache the real providers for use in addTestProvider() and removeTestProvider()
- LocationProviderProxy mNetworkLocationProvider;
- LocationProviderInterface mGpsLocationProvider;
+ // --- fields below are protected by mWakeLock ---
+ private int mPendingBroadcasts;
- // Handler messages
- private static final int MESSAGE_LOCATION_CHANGED = 1;
- private static final int MESSAGE_PACKAGE_UPDATED = 2;
+ // --- fields below are protected by mLock ---
+ // Set of providers that are explicitly enabled
+ private final Set<String> mEnabledProviders = new HashSet<String>();
- // wakelock variables
- private final static String WAKELOCK_KEY = "LocationManagerService";
- private PowerManager.WakeLock mWakeLock = null;
- private int mPendingBroadcasts;
+ // Set of providers that are explicitly disabled
+ private final Set<String> mDisabledProviders = new HashSet<String>();
- /**
- * List of all receivers.
- */
- private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
+ // Mock (test) providers
+ private final HashMap<String, MockProvider> mMockProviders =
+ new HashMap<String, MockProvider>();
+ // all receivers
+ private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
- /**
- * List of location providers.
- */
+ // currently installed providers (with mocks replacing real providers)
private final ArrayList<LocationProviderInterface> mProviders =
- new ArrayList<LocationProviderInterface>();
- private final HashMap<String, LocationProviderInterface> mProvidersByName
- = new HashMap<String, LocationProviderInterface>();
+ new ArrayList<LocationProviderInterface>();
- /**
- * Object used internally for synchronization
- */
- private final Object mLock = new Object();
+ // real providers, saved here when mocked out
+ private final HashMap<String, LocationProviderInterface> mRealProviders =
+ new HashMap<String, LocationProviderInterface>();
- /**
- * Mapping from provider name to all its UpdateRecords
- */
- private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
- new HashMap<String,ArrayList<UpdateRecord>>();
+ // mapping from provider name to provider
+ private final HashMap<String, LocationProviderInterface> mProvidersByName =
+ new HashMap<String, LocationProviderInterface>();
- /**
- * Temporary filled in when computing min time for a provider. Access is
- * protected by global lock mLock.
- */
- private final WorkSource mTmpWorkSource = new WorkSource();
+ // mapping from provider name to all its UpdateRecords
+ private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
+ new HashMap<String, ArrayList<UpdateRecord>>();
+
+ // mapping from provider name to last known location
+ private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
+
+ // all providers that operate over proxy, for authorizing incoming location
+ private final ArrayList<LocationProviderProxy> mProxyProviders =
+ new ArrayList<LocationProviderProxy>();
+
+ public LocationManagerService(Context context) {
+ super();
+ mContext = context;
+
+ if (D) Log.d(TAG, "Constructed");
- GeofenceManager mGeofenceManager;
+ // most startup is deferred until systemReady()
+ }
- // Last known location for each provider
- private HashMap<String,Location> mLastKnownLocation =
- new HashMap<String,Location>();
+ public void systemReady() {
+ Thread thread = new Thread(null, this, THREAD_NAME);
+ thread.start();
+ }
- private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ Looper.prepare();
+ mLocationHandler = new LocationWorkerHandler();
+ init();
+ Looper.loop();
+ }
- // for Settings change notification
- private ContentQueryMap mSettings;
+ private void init() {
+ if (D) Log.d(TAG, "init()");
+
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+ mPackageManager = mContext.getPackageManager();
+
+ synchronized (mLock) {
+ loadProvidersLocked();
+ }
+ mGeofenceManager = new GeofenceManager(mContext);
+
+ // Register for Network (Wifi or Mobile) updates
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+
+ // listen for settings changes
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
+ "(" + NameValueTable.NAME + "=?)",
+ new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
+ ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
+ mLocationHandler);
+ settingsCursor.close();
+ query.addObserver(this);
+ mPackageMonitor.register(mContext, Looper.myLooper(), true);
+ }
+
+ private void loadProvidersLocked() {
+ if (GpsLocationProvider.isSupported()) {
+ // Create a gps location provider
+ GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
+ mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
+ mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
+ addProviderLocked(gpsProvider);
+ mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
+ }
+
+ // create a passive location provider, which is always enabled
+ PassiveProvider passiveProvider = new PassiveProvider(this);
+ addProviderLocked(passiveProvider);
+ mEnabledProviders.add(passiveProvider.getName());
+ mPassiveProvider = passiveProvider;
+
+ /*
+ Load package name(s) containing location provider support.
+ These packages can contain services implementing location providers:
+ Geocoder Provider, Network Location Provider, and
+ Fused Location Provider. They will each be searched for
+ service components implementing these providers.
+ The location framework also has support for installation
+ of new location providers at run-time. The new package does not
+ have to be explicitly listed here, however it must have a signature
+ that matches the signature of at least one package on this list.
+ */
+ Resources resources = mContext.getResources();
+ ArrayList<String> providerPackageNames = new ArrayList<String>();
+ String[] pkgs1 = resources.getStringArray(
+ com.android.internal.R.array.config_locationProviderPackageNames);
+ String[] pkgs2 = resources.getStringArray(
+ com.android.internal.R.array.config_overlay_locationProviderPackageNames);
+ if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
+ if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
+ if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
+ if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
+
+ // bind to network provider
+ LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
+ mContext,
+ LocationManager.NETWORK_PROVIDER,
+ NETWORK_LOCATION_SERVICE_ACTION,
+ providerPackageNames, mLocationHandler);
+ if (networkProvider != null) {
+ mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+ mProxyProviders.add(networkProvider);
+ addProviderLocked(networkProvider);
+ } else {
+ Slog.w(TAG, "no network location provider found");
+ }
+
+ // bind to fused provider
+ LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+ mContext,
+ LocationManager.FUSED_PROVIDER,
+ FUSED_LOCATION_SERVICE_ACTION,
+ providerPackageNames, mLocationHandler);
+ if (fusedLocationProvider != null) {
+ addProviderLocked(fusedLocationProvider);
+ mProxyProviders.add(fusedLocationProvider);
+ mEnabledProviders.add(fusedLocationProvider.getName());
+ } else {
+ Slog.e(TAG, "no fused location provider found",
+ new IllegalStateException("Location service needs a fused location provider"));
+ }
+
+ // bind to geocoder provider
+ mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
+ if (mGeocodeProvider == null) {
+ Slog.e(TAG, "no geocoder provider found");
+ }
+
+ updateProvidersLocked();
+ }
/**
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
*/
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
+ final int mUid; // uid of receiver
+ final int mPid; // pid of receiver
+ final String mPackageName; // package name of receiver
+ final String mPermission; // best permission that receiver has
+
final ILocationListener mListener;
final PendingIntent mPendingIntent;
final Object mKey;
+
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
int mPendingBroadcasts;
- String mRequiredPermissions;
- Receiver(ILocationListener listener) {
+ Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+ String packageName) {
mListener = listener;
- mPendingIntent = null;
- mKey = listener.asBinder();
- }
-
- Receiver(PendingIntent intent) {
mPendingIntent = intent;
- mListener = null;
- mKey = intent;
+ if (listener != null) {
+ mKey = listener.asBinder();
+ } else {
+ mKey = intent;
+ }
+ mPermission = checkPermission();
+ mUid = uid;
+ mPid = pid;
+ mPackageName = packageName;
}
@Override
public boolean equals(Object otherObj) {
if (otherObj instanceof Receiver) {
- return mKey.equals(
- ((Receiver)otherObj).mKey);
+ return mKey.equals(((Receiver)otherObj).mKey);
}
return false;
}
@@ -223,18 +362,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public String toString() {
- String result;
+ StringBuilder s = new StringBuilder();
+ s.append("Reciever[");
+ s.append(Integer.toHexString(System.identityHashCode(this)));
if (mListener != null) {
- result = "Receiver{"
- + Integer.toHexString(System.identityHashCode(this))
- + " Listener " + mKey + "}";
+ s.append(" listener");
} else {
- result = "Receiver{"
- + Integer.toHexString(System.identityHashCode(this))
- + " Intent " + mKey + "}";
+ s.append(" intent");
}
- result += "mUpdateRecords: " + mUpdateRecords;
- return result;
+ for (String p : mUpdateRecords.keySet()) {
+ s.append(" ").append(mUpdateRecords.get(p).toString());
+ }
+ s.append("]");
+ return s.toString();
}
public boolean isListener() {
@@ -275,7 +415,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -309,7 +449,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -347,7 +487,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -361,9 +501,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public void binderDied() {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Location listener died");
- }
+ if (D) Log.d(TAG, "Location listener died");
+
synchronized (mLock) {
removeUpdatesLocked(this);
}
@@ -416,200 +555,25 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private final class SettingsObserver implements Observer {
- @Override
- public void update(Observable o, Object arg) {
- synchronized (mLock) {
- updateProvidersLocked();
- }
+ /** Settings Observer callback */
+ @Override
+ public void update(Observable o, Object arg) {
+ synchronized (mLock) {
+ updateProvidersLocked();
}
}
- private void addProvider(LocationProviderInterface provider) {
+ private void addProviderLocked(LocationProviderInterface provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProvider(LocationProviderInterface provider) {
+ private void removeProviderLocked(LocationProviderInterface provider) {
+ provider.disable();
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
- private void loadProviders() {
- synchronized (mLock) {
- if (sProvidersLoaded) {
- return;
- }
-
- // Load providers
- loadProvidersLocked();
- sProvidersLoaded = true;
- }
- }
-
- private void loadProvidersLocked() {
- try {
- _loadProvidersLocked();
- } catch (Exception e) {
- Slog.e(TAG, "Exception loading providers:", e);
- }
- }
-
- private void _loadProvidersLocked() {
- // Attempt to load "real" providers first
- if (GpsLocationProvider.isSupported()) {
- // Create a gps location provider
- GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
- mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
- mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
- addProvider(gpsProvider);
- mGpsLocationProvider = gpsProvider;
- }
-
- // create a passive location provider, which is always enabled
- PassiveProvider passiveProvider = new PassiveProvider(this);
- addProvider(passiveProvider);
- mEnabledProviders.add(passiveProvider.getName());
-
- // initialize external network location and geocoder services.
- // The initial value of mNetworkLocationProviderPackageName and
- // mGeocodeProviderPackageName is just used to determine what
- // signatures future mNetworkLocationProviderPackageName and
- // mGeocodeProviderPackageName packages must have. So alternate
- // providers can be installed under a different package name
- // so long as they have the same signature as the original
- // provider packages.
- if (mNetworkLocationProviderPackageName != null) {
- String packageName = findBestPackage(LocationProviderProxy.SERVICE_ACTION,
- mNetworkLocationProviderPackageName);
- if (packageName != null) {
- mNetworkLocationProvider = new LocationProviderProxy(mContext,
- LocationManager.NETWORK_PROVIDER,
- packageName, mLocationHandler);
- mNetworkLocationProviderPackageName = packageName;
- addProvider(mNetworkLocationProvider);
- }
- }
- if (mGeocodeProviderPackageName != null) {
- String packageName = findBestPackage(GeocoderProxy.SERVICE_ACTION,
- mGeocodeProviderPackageName);
- if (packageName != null) {
- mGeocodeProvider = new GeocoderProxy(mContext, packageName);
- mGeocodeProviderPackageName = packageName;
- }
- }
-
- updateProvidersLocked();
- }
-
- /**
- * Pick the best (network location provider or geocode provider) package.
- * The best package:
- * - implements serviceIntentName
- * - has signatures that match that of sigPackageName
- * - has the highest version value in a meta-data field in the service component
- */
- String findBestPackage(String serviceIntentName, String sigPackageName) {
- Intent intent = new Intent(serviceIntentName);
- List<ResolveInfo> infos = mPackageManager.queryIntentServices(intent,
- PackageManager.GET_META_DATA);
- if (infos == null) return null;
-
- int bestVersion = Integer.MIN_VALUE;
- String bestPackage = null;
- for (ResolveInfo info : infos) {
- String packageName = info.serviceInfo.packageName;
- // check signature
- if (mPackageManager.checkSignatures(packageName, sigPackageName) !=
- PackageManager.SIGNATURE_MATCH) {
- Slog.w(TAG, packageName + " implements " + serviceIntentName +
- " but its signatures don't match those in " + sigPackageName +
- ", ignoring");
- continue;
- }
- // read version
- int version = 0;
- if (info.serviceInfo.metaData != null) {
- version = info.serviceInfo.metaData.getInt("version", 0);
- }
- if (LOCAL_LOGV) Slog.v(TAG, packageName + " implements " + serviceIntentName +
- " with version " + version);
- if (version > bestVersion) {
- bestVersion = version;
- bestPackage = packageName;
- }
- }
-
- return bestPackage;
- }
-
- /**
- * @param context the context that the LocationManagerService runs in
- */
- public LocationManagerService(Context context) {
- super();
- mContext = context;
- Resources resources = context.getResources();
-
- mNetworkLocationProviderPackageName = resources.getString(
- com.android.internal.R.string.config_networkLocationProviderPackageName);
- mGeocodeProviderPackageName = resources.getString(
- com.android.internal.R.string.config_geocodeProviderPackageName);
-
- mPackageMonitor.register(context, null, true);
-
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Constructed LocationManager Service");
- }
- }
-
- void systemReady() {
- // we defer starting up the service until the system is ready
- Thread thread = new Thread(null, this, "LocationManagerService");
- thread.start();
- }
-
- private void initialize() {
- // Create a wake lock, needs to be done before calling loadProviders() below
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- mPackageManager = mContext.getPackageManager();
-
- // Load providers
- loadProviders();
-
- // Register for Network (Wifi or Mobile) updates
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- // Register for Package Manager updates
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
-
- // listen for settings changes
- ContentResolver resolver = mContext.getContentResolver();
- Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
- "(" + NameValueTable.NAME + "=?)",
- new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
- null);
- mSettings = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true, mLocationHandler);
- SettingsObserver settingsObserver = new SettingsObserver();
- mSettings.addObserver(settingsObserver);
- }
-
- @Override
- public void run()
- {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- Looper.prepare();
- mLocationHandler = new LocationWorkerHandler();
- initialize();
- mGeofenceManager = new GeofenceManager(mContext);
- Looper.loop();
- }
private boolean isAllowedBySettingsLocked(String provider) {
if (mEnabledProviders.contains(provider)) {
@@ -624,324 +588,131 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return Settings.Secure.isLocationProviderEnabled(resolver, provider);
}
- private String checkPermissionsSafe(String provider, String lastPermission) {
- if (LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider)) {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION permission");
- }
- return ACCESS_FINE_LOCATION;
- }
-
- // Assume any other provider requires the coarse or fine permission.
- if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
- return ACCESS_FINE_LOCATION.equals(lastPermission)
- ? lastPermission : ACCESS_COARSE_LOCATION;
- }
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
+ /**
+ * Throw SecurityException if caller has neither COARSE or FINE.
+ * Otherwise, return the best permission.
+ */
+ private String checkPermission() {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED) {
return ACCESS_FINE_LOCATION;
+ } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED) {
+ return ACCESS_COARSE_LOCATION;
}
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
- }
-
- private boolean isAllowedProviderSafe(String provider) {
- if ((LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider))
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
- }
- if (LocationManager.NETWORK_PROVIDER.equals(provider)
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)
- && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
- }
-
- return true;
+ throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
+ "ACCESS_FINE_LOCATION permission");
}
+ /**
+ * Returns all providers by name, including passive, but excluding
+ * fused.
+ */
@Override
public List<String> getAllProviders() {
- try {
- synchronized (mLock) {
- return _getAllProvidersLocked();
+ checkPermission();
+
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<String>(mProviders.size());
+ for (LocationProviderInterface provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
+ out.add(name);
}
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getAllProviders got exception:", e);
- return null;
}
- }
- private List<String> _getAllProvidersLocked() {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getAllProviders");
- }
- ArrayList<String> out = new ArrayList<String>(mProviders.size());
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- out.add(p.getName());
- }
+ if (D) Log.d(TAG, "getAllProviders()=" + out);
return out;
}
+ /**
+ * Return all providers by name, that match criteria and are optionally
+ * enabled.
+ * Can return passive provider, but never returns fused provider.
+ */
@Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
- try {
- synchronized (mLock) {
- return _getProvidersLocked(criteria, enabledOnly);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getProviders got exception:", e);
- return null;
- }
- }
+ checkPermission();
- private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getProviders");
- }
- ArrayList<String> out = new ArrayList<String>(mProviders.size());
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- String name = p.getName();
- if (isAllowedProviderSafe(name)) {
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<String>(mProviders.size());
+ for (LocationProviderInterface provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
if (enabledOnly && !isAllowedBySettingsLocked(name)) {
continue;
}
- if (criteria != null && !p.meetsCriteria(criteria)) {
+ if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+ name, provider.getProperties(), 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> {
- @Override
- 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> {
- @Override
- 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);
- }
-
- @Override
- 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>(providerNames.size());
- for (String name : providerNames) {
- providers.add(mProvidersByName.get(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;
-
- 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);
+ if (D) Log.d(TAG, "getProviders()=" + out);
+ return out;
}
/**
- * 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
+ * Return the name of the best provider given a Criteria object.
+ * This method has been deprecated from the public API,
+ * and the whole LoactionProvider (including #meetsCriteria)
+ * has been deprecated as well. So this method now uses
+ * some simplified logic.
*/
@Override
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- List<String> goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
+ String result = null;
+ checkPermission();
- // 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();
+ List<String> providers = getProviders(criteria, enabledOnly);
+ if (providers.size() < 1) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
-
- // Remove bearing requirement
- criteria.setBearingRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
+ providers = getProviders(null, enabledOnly);
+ if (providers.size() >= 1) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
- // Remove speed requirement
- criteria.setSpeedRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return null;
+ }
- // Remove altitude requirement
- criteria.setAltitudeRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
+ private String pickBest(List<String> providers) {
+ if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+ return LocationManager.NETWORK_PROVIDER;
+ } else if (providers.contains(LocationManager.GPS_PROVIDER)) {
+ return LocationManager.GPS_PROVIDER;
+ } else {
+ return providers.get(0);
}
-
- return null;
}
@Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ checkPermission();
+
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
}
- return p.meetsCriteria(criteria);
+
+ boolean result = LocationProvider.propertiesMeetCriteria(
+ p.getName(), p.getProperties(), criteria);
+ if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
+ return result;
}
private void updateProvidersLocked() {
@@ -968,16 +739,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
int listeners = 0;
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return;
- }
+ if (p == null) return;
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records != null) {
final int N = records.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
UpdateRecord record = records.get(i);
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
@@ -991,7 +760,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
if (deadReceivers != null) {
- for (int i=deadReceivers.size()-1; i>=0; i--) {
+ for (int i = deadReceivers.size() - 1; i >= 0; i--) {
removeUpdatesLocked(deadReceivers.get(i));
}
}
@@ -999,59 +768,70 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (enabled) {
p.enable();
if (listeners > 0) {
- p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
- p.enableLocationTracking(true);
+ applyRequirementsLocked(provider);
}
} else {
- p.enableLocationTracking(false);
p.disable();
}
}
- private long getMinTimeLocked(String provider) {
- long minTime = Long.MAX_VALUE;
+ private void applyRequirementsLocked(String provider) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) return;
+
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- mTmpWorkSource.clear();
+ WorkSource worksource = new WorkSource();
+ ProviderRequest providerRequest = new ProviderRequest();
+
if (records != null) {
- for (int i=records.size()-1; i>=0; i--) {
- UpdateRecord ur = records.get(i);
- long curTime = ur.mMinTime;
- if (curTime < minTime) {
- minTime = curTime;
+ for (UpdateRecord record : records) {
+ LocationRequest locationRequest = record.mRequest;
+
+ if (providerRequest.locationRequests == null) {
+ providerRequest.locationRequests = new ArrayList<LocationRequest>();
+ }
+
+ providerRequest.locationRequests.add(locationRequest);
+ if (locationRequest.getInterval() < providerRequest.interval) {
+ providerRequest.reportLocation = true;
+ providerRequest.interval = locationRequest.getInterval();
}
}
- long inclTime = (minTime*3)/2;
- for (int i=records.size()-1; i>=0; i--) {
- UpdateRecord ur = records.get(i);
- if (ur.mMinTime <= inclTime) {
- mTmpWorkSource.add(ur.mUid);
+
+ if (providerRequest.reportLocation) {
+ // calculate who to blame for power
+ // This is somewhat arbitrary. We pick a threshold interval
+ // that is slightly higher that the minimum interval, and
+ // spread the blame across all applications with a request
+ // under that threshold.
+ long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
+ for (UpdateRecord record : records) {
+ LocationRequest locationRequest = record.mRequest;
+ if (locationRequest.getInterval() <= thresholdInterval) {
+ worksource.add(record.mReceiver.mUid);
+ }
}
}
}
- return minTime;
+
+ if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
+ p.setRequest(providerRequest, worksource);
}
private class UpdateRecord {
final String mProvider;
+ final LocationRequest mRequest;
final Receiver mReceiver;
- final long mMinTime;
- final float mMinDistance;
- final boolean mSingleShot;
- final int mUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot,
- Receiver receiver, int uid) {
+ UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
mProvider = provider;
+ mRequest = request;
mReceiver = receiver;
- mMinTime = minTime;
- mMinDistance = minDistance;
- mSingleShot = singleShot;
- mUid = uid;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
@@ -1067,45 +847,49 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
* Method to be called when a record will no longer be used. Calling this multiple times
* must have the same effect as calling it once.
*/
- void disposeLocked() {
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
- if (records != null) {
- records.remove(this);
+ void disposeLocked(boolean removeReceiver) {
+ // remove from mRecordsByProvider
+ ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
+ if (globalRecords != null) {
+ globalRecords.remove(this);
+ }
+
+ if (!removeReceiver) return; // the caller will handle the rest
+
+ // remove from Receiver#mUpdateRecords
+ HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
+ if (receiverRecords != null) {
+ receiverRecords.remove(this.mProvider);
+
+ // and also remove the Receiver if it has no more update records
+ if (removeReceiver && receiverRecords.size() == 0) {
+ removeUpdatesLocked(mReceiver);
+ }
}
}
@Override
public String toString() {
- return "UpdateRecord{"
- + Integer.toHexString(System.identityHashCode(this))
- + " mProvider: " + mProvider + " mUid: " + mUid + "}";
- }
-
- void dump(PrintWriter pw, String prefix) {
- 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) {
- mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
- }
- pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
+ StringBuilder s = new StringBuilder();
+ s.append("UpdateRecord[");
+ s.append(mProvider);
+ s.append(' ').append(mReceiver.mPackageName).append('(');
+ s.append(mReceiver.mUid).append(')');
+ s.append(' ').append(mRequest);
+ s.append(']');
+ return s.toString();
}
}
- private Receiver getReceiver(ILocationListener listener) {
+ private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
- receiver = new Receiver(listener);
+ receiver = new Receiver(listener, null, pid, uid, packageName);
mReceivers.put(binder, receiver);
try {
- if (receiver.isListener()) {
- receiver.getListener().asBinder().linkToDeath(receiver, 0);
- }
+ receiver.getListener().asBinder().linkToDeath(receiver, 0);
} catch (RemoteException e) {
Slog.e(TAG, "linkToDeath failed:", e);
return null;
@@ -1114,58 +898,29 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return receiver;
}
- private Receiver getReceiver(PendingIntent intent) {
+ private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
- receiver = new Receiver(intent);
+ receiver = new Receiver(null, intent, pid, uid, packageName);
mReceivers.put(intent, receiver);
}
return receiver;
}
- private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records != null) {
- for (int i = records.size() - 1; i >= 0; i--) {
- UpdateRecord record = records.get(i);
- if (record.mUid == uid && record.mReceiver != excludedReceiver) {
- return true;
- }
- }
- }
- return false;
- }
+ private String checkPermissionAndRequest(LocationRequest request) {
+ String perm = checkPermission();
- @Override
- 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, singleShot,
- getReceiver(listener));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "requestUpdates got exception:", e);
+ if (ACCESS_COARSE_LOCATION.equals(perm)) {
+ request.applyCoarsePermissionRestrictions();
}
+ return perm;
}
- void validatePackageName(int uid, String packageName) {
+ private void checkPackageName(String packageName) {
if (packageName == null) {
- throw new SecurityException("packageName cannot be null");
+ throw new SecurityException("invalid package name: " + packageName);
}
+ int uid = Binder.getCallingUid();
String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages == null) {
throw new SecurityException("invalid UID " + uid);
@@ -1173,202 +928,188 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (String pkg : packages) {
if (packageName.equals(pkg)) return;
}
- throw new SecurityException("invalid package name");
+ throw new SecurityException("invalid package name: " + packageName);
}
- void validatePendingIntent(PendingIntent intent) {
- if (intent.isTargetedToPackage()) {
- return;
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + intent);
}
- Slog.i(TAG, "Given Intent does not require a specific package: "
- + intent);
- // XXX we should really throw a security exception, if the caller's
- // targetSdkVersion is high enough.
- //throw new SecurityException("Given Intent does not require a specific package: "
- // + intent);
}
- @Override
- public void requestLocationUpdatesPI(String provider, Criteria criteria,
- long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
- validatePendingIntent(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, singleShot,
- getReceiver(intent));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "requestUpdates got exception:", e);
+ private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
+ int pid, int uid, String packageName) {
+ if (intent == null && listener == null) {
+ throw new IllegalArgumentException("need eiter listener or intent");
+ } else if (intent != null && listener != null) {
+ throw new IllegalArgumentException("cannot register both listener and intent");
+ } else if (intent != null) {
+ checkPendingIntent(intent);
+ return getReceiver(intent, pid, uid, packageName);
+ } else {
+ return getReceiver(listener, pid, uid, packageName);
}
}
- private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance,
- boolean singleShot, Receiver receiver) {
+ @Override
+ public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
+ PendingIntent intent, String packageName) {
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ checkPackageName(packageName);
+ checkPermissionAndRequest(request);
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- throw new IllegalArgumentException("requested provider " + provider +
- " doesn't exisit");
- }
- receiver.mRequiredPermissions = checkPermissionsSafe(provider,
- receiver.mRequiredPermissions);
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
- // so wakelock calls will succeed
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
- boolean newUid = !providerHasListener(provider, callingUid, null);
+ // so wakelock calls will succeed (not totally sure this is still needed)
long identity = Binder.clearCallingIdentity();
try {
- UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot,
- receiver, callingUid);
- UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
- if (oldRecord != null) {
- oldRecord.disposeLocked();
- }
-
- if (newUid) {
- p.addListener(callingUid);
- }
-
- boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
- if (isProviderEnabled) {
- long minTimeForProvider = getMinTimeLocked(provider);
- Slog.i(TAG, "request " + provider + " (pid " + callingPid + ") " + minTime +
- " " + minTimeForProvider + (singleShot ? " (singleshot)" : ""));
- p.setMinTime(minTimeForProvider, mTmpWorkSource);
- // 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);
- }
- if (LOCAL_LOGV) {
- Slog.v(TAG, "_requestLocationUpdates: provider = " + provider + " listener = " + receiver);
+ synchronized (mLock) {
+ requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- @Override
- public void removeUpdates(ILocationListener listener) {
- try {
- synchronized (mLock) {
- removeUpdatesLocked(getReceiver(listener));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeUpdates got exception:", e);
+ private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
+ int pid, int uid, String packageName) {
+ // Figure out the provider. Either its explicitly request (legacy use cases), or
+ // use the fused provider
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProviderInterface provider = mProvidersByName.get(name);
+ if (provider == null) {
+ throw new IllegalArgumentException("provider doesn't exisit: " + provider);
+ }
+
+ Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
+ name + " " + request + " from " + packageName + "(" + uid + ")");
+
+ UpdateRecord record = new UpdateRecord(name, request, receiver);
+ UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
+ if (oldRecord != null) {
+ oldRecord.disposeLocked(false);
+ }
+
+ boolean isProviderEnabled = isAllowedBySettingsLocked(name);
+ if (isProviderEnabled) {
+ applyRequirementsLocked(name);
+ } else {
+ // Notify the listener that updates are currently disabled
+ receiver.callProviderEnabledLocked(name, false);
}
}
@Override
- public void removeUpdatesPI(PendingIntent intent) {
+ public void removeUpdates(ILocationListener listener, PendingIntent intent,
+ String packageName) {
+ checkPackageName(packageName);
+ checkPermission();
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
+
+ // so wakelock calls will succeed (not totally sure this is still needed)
+ long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- removeUpdatesLocked(getReceiver(intent));
+ removeUpdatesLocked(receiver);
}
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeUpdates got exception:", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
private void removeUpdatesLocked(Receiver receiver) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "_removeUpdates: listener = " + receiver);
- }
+ Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
- // so wakelock calls will succeed
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
- receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
- synchronized(receiver) {
- if(receiver.mPendingBroadcasts > 0) {
- decrementPendingBroadcasts();
- receiver.mPendingBroadcasts = 0;
- }
+ if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
+ receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
+ synchronized (receiver) {
+ if (receiver.mPendingBroadcasts > 0) {
+ decrementPendingBroadcasts();
+ receiver.mPendingBroadcasts = 0;
}
}
+ }
- // Record which providers were associated with this listener
- HashSet<String> providers = new HashSet<String>();
- HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
- if (oldRecords != null) {
- // Call dispose() on the obsolete update records.
- for (UpdateRecord record : oldRecords.values()) {
- if (!providerHasListener(record.mProvider, callingUid, receiver)) {
- LocationProviderInterface p = mProvidersByName.get(record.mProvider);
- if (p != null) {
- p.removeListener(callingUid);
- }
- }
- record.disposeLocked();
- }
- // Accumulate providers
- providers.addAll(oldRecords.keySet());
+ // Record which providers were associated with this listener
+ HashSet<String> providers = new HashSet<String>();
+ HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
+ if (oldRecords != null) {
+ // Call dispose() on the obsolete update records.
+ for (UpdateRecord record : oldRecords.values()) {
+ record.disposeLocked(false);
}
+ // Accumulate providers
+ providers.addAll(oldRecords.keySet());
+ }
- // See if the providers associated with this listener have any
- // other listeners; if one does, inform it of the new smallest minTime
- // value; if one does not, disable location tracking for it
- for (String provider : providers) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedBySettingsLocked(provider)) {
- continue;
- }
+ // update provider
+ for (String provider : providers) {
+ // If provider is already disabled, don't need to do anything
+ if (!isAllowedBySettingsLocked(provider)) {
+ continue;
+ }
- boolean hasOtherListener = false;
- ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
- if (recordsForProvider != null && recordsForProvider.size() > 0) {
- hasOtherListener = true;
- }
+ applyRequirementsLocked(provider);
+ }
+ }
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p != null) {
- if (hasOtherListener) {
- long minTime = getMinTimeLocked(provider);
- Slog.i(TAG, "remove " + provider + " (pid " + callingPid +
- "), next minTime = " + minTime);
- p.setMinTime(minTime, mTmpWorkSource);
- } else {
- Slog.i(TAG, "remove " + provider + " (pid " + callingPid +
- "), disabled");
- p.enableLocationTracking(false);
- }
- }
+ @Override
+ public Location getLastLocation(LocationRequest request) {
+ if (D) Log.d(TAG, "getLastLocation: " + request);
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ String perm = checkPermissionAndRequest(request);
+
+ synchronized (mLock) {
+ // Figure out the provider. Either its explicitly request (deprecated API's),
+ // or use the fused provider
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProviderInterface provider = mProvidersByName.get(name);
+ if (provider == null) return null;
+
+ if (!isAllowedBySettingsLocked(name)) return null;
+
+ Location location = mLastLocation.get(name);
+ if (ACCESS_FINE_LOCATION.equals(perm)) {
+ return location;
+ } else {
+ return getCoarseLocationExtra(location);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
@Override
+ public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
+ String packageName) {
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ checkPermissionAndRequest(request);
+ checkPendingIntent(intent);
+ checkPackageName(packageName);
+
+ if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
+
+ mGeofenceManager.addFence(request, geofence, intent, Binder.getCallingUid(), packageName);
+ }
+
+ @Override
+ public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
+ checkPermission();
+ checkPendingIntent(intent);
+ checkPackageName(packageName);
+
+ if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
+
+ mGeofenceManager.removeFence(geofence, intent);
+ }
+
+
+ @Override
public boolean addGpsStatusListener(IGpsStatusListener listener) {
if (mGpsStatusProvider == null) {
return false;
@@ -1405,8 +1146,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new NullPointerException();
}
- // first check for permission to the provider
- checkPermissionsSafe(provider, null);
+ checkPermission();
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -1415,176 +1155,113 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (mLock) {
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return false;
- }
+ if (p == null) return false;
return p.sendExtraCommand(command, extras);
}
}
@Override
- public boolean sendNiResponse(int notifId, int userResponse)
- {
+ public boolean sendNiResponse(int notifId, int userResponse) {
if (Binder.getCallingUid() != Process.myUid()) {
throw new SecurityException(
"calling sendNiResponse from outside of the system is not allowed");
}
try {
return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
- }
- catch (RemoteException e)
- {
+ } catch (RemoteException e) {
Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
return false;
}
}
- @Override
- public void addProximityAlert(double latitude, double longitude,
- float radius, long expiration, PendingIntent intent, String packageName) {
- validatePendingIntent(intent);
- validatePackageName(Binder.getCallingUid(), packageName);
-
- // Require ability to access all providers for now
- if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
- !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
- throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
- }
-
- if (LOCAL_LOGV) Slog.v(TAG, "addProximityAlert: lat=" + latitude + ", long=" + longitude +
- ", radius=" + radius + ", exp=" + expiration + ", intent = " + intent);
-
- mGeofenceManager.addFence(latitude, longitude, radius, expiration, intent,
- Binder.getCallingUid(), packageName);
- }
-
- @Override
- public void removeProximityAlert(PendingIntent intent) {
- if (intent == null) throw new NullPointerException("pending intent is null");
-
- if (LOCAL_LOGV) Slog.v(TAG, "removeProximityAlert: intent = " + intent);
-
- mGeofenceManager.removeFence(intent);
- }
-
/**
* @return null if the provider does not exist
* @throws SecurityException if the provider is not allowed to be
* accessed by the caller
*/
@Override
- public Bundle getProviderInfo(String provider) {
- try {
- synchronized (mLock) {
- return _getProviderInfoLocked(provider);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "_getProviderInfo got exception:", e);
- return null;
- }
- }
+ public ProviderProperties getProviderProperties(String provider) {
+ checkPermission();
- private Bundle _getProviderInfoLocked(String provider) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return null;
+ LocationProviderInterface p;
+ synchronized (mLock) {
+ p = mProvidersByName.get(provider);
}
- checkPermissionsSafe(provider, null);
-
- Bundle b = new Bundle();
- b.putBoolean("network", p.requiresNetwork());
- b.putBoolean("satellite", p.requiresSatellite());
- b.putBoolean("cell", p.requiresCell());
- b.putBoolean("cost", p.hasMonetaryCost());
- b.putBoolean("altitude", p.supportsAltitude());
- b.putBoolean("speed", p.supportsSpeed());
- b.putBoolean("bearing", p.supportsBearing());
- b.putInt("power", p.getPowerRequirement());
- b.putInt("accuracy", p.getAccuracy());
-
- return b;
+ if (p == null) return null;
+ return p.getProperties();
}
@Override
public boolean isProviderEnabled(String provider) {
- try {
- synchronized (mLock) {
- return _isProviderEnabledLocked(provider);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "isProviderEnabled got exception:", e);
- return false;
+ checkPermission();
+ if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+
+ synchronized (mLock) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) return false;
+
+ return isAllowedBySettingsLocked(provider);
}
}
- @Override
- public void reportLocation(Location location, boolean passive) {
+ private void checkCallerIsProvider() {
if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
- }
-
- if (!location.isComplete()) {
- Log.w(TAG, "Dropping incomplete location: " + location);
+ == PackageManager.PERMISSION_GRANTED) {
return;
}
- mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
- Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
- m.arg1 = (passive ? 1 : 0);
- mLocationHandler.sendMessageAtFrontOfQueue(m);
- }
+ // Previously we only used the INSTALL_LOCATION_PROVIDER
+ // check. But that is system or signature
+ // protection level which is not flexible enough for
+ // providers installed oustide the system image. So
+ // also allow providers with a UID matching the
+ // currently bound package name
- private boolean _isProviderEnabledLocked(String provider) {
- checkPermissionsSafe(provider, null);
+ int uid = Binder.getCallingUid();
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return false;
+ if (mGeocodeProvider != null) {
+ if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
+ }
+ for (LocationProviderProxy proxy : mProxyProviders) {
+ if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
}
- return isAllowedBySettingsLocked(provider);
+ throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
+ "or UID of a currently bound location provider");
}
- @Override
- public Location getLastKnownLocation(String provider) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getLastKnownLocation: " + provider);
+ private boolean doesPackageHaveUid(int uid, String packageName) {
+ if (packageName == null) {
+ return false;
}
try {
- synchronized (mLock) {
- return _getLastKnownLocationLocked(provider);
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
+ if (appInfo.uid != uid) {
+ return false;
}
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getLastKnownLocation got exception:", e);
- return null;
+ } catch (NameNotFoundException e) {
+ return false;
}
+ return true;
}
- private Location _getLastKnownLocationLocked(String provider) {
- checkPermissionsSafe(provider, null);
-
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return null;
- }
+ @Override
+ public void reportLocation(Location location, boolean passive) {
+ checkCallerIsProvider();
- if (!isAllowedBySettingsLocked(provider)) {
- return null;
+ if (!location.isComplete()) {
+ Log.w(TAG, "Dropping incomplete location: " + location);
+ return;
}
- return mLastKnownLocation.get(provider);
+ mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
+ Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
+ m.arg1 = (passive ? 1 : 0);
+ mLocationHandler.sendMessageAtFrontOfQueue(m);
}
+
private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
// Always broadcast the first update
if (lastLoc == null) {
@@ -1592,14 +1269,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
// Check whether sufficient time has passed
- long minTime = record.mMinTime;
+ long minTime = record.mRequest.getFastestInterval();
long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
- if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
+ if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
return false;
}
// Check whether sufficient distance has been traveled
- double minDistance = record.mMinDistance;
+ double minDistance = record.mRequest.getSmallestDisplacement();
if (minDistance > 0.0) {
if (loc.distanceTo(lastLoc) <= minDistance) {
return false;
@@ -1610,24 +1287,27 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private void handleLocationChangedLocked(Location location, boolean passive) {
+ long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records == null || records.size() == 0) {
- return;
- }
+ if (records == null || records.size() == 0) return;
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return;
+ if (p == null) return;
+
+ // Add the coarse location as an extra, if not already present
+ Location coarse = getCoarseLocationExtra(location);
+ if (coarse == null) {
+ coarse = addCoarseLocationExtra(location);
}
- // Update last known location for provider
- Location lastLocation = mLastKnownLocation.get(provider);
+ // Update last known locations
+ Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
- mLastKnownLocation.put(provider, new Location(location));
- } else {
- lastLocation.set(location);
+ lastLocation = new Location(provider);
+ mLastLocation.put(provider, lastLocation);
}
+ lastLocation.set(location);
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
@@ -1637,13 +1317,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
int status = p.getStatus(extras);
ArrayList<Receiver> deadReceivers = null;
+ ArrayList<UpdateRecord> deadUpdateRecords = null;
// Broadcast location or status to all listeners
- final int N = records.size();
- for (int i=0; i<N; i++) {
- UpdateRecord r = records.get(i);
+ for (UpdateRecord r : records) {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
+ if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
+ location = lastLocation; // use fine location
+ } else {
+ location = coarse; // use coarse location
+ }
Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
@@ -1670,8 +1354,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- // remove receiver if it is dead or we just processed a single shot request
- if (receiverDead || r.mSingleShot) {
+ // track expired records
+ if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
+ if (deadUpdateRecords == null) {
+ deadUpdateRecords = new ArrayList<UpdateRecord>();
+ }
+ deadUpdateRecords.add(r);
+ }
+ // track dead receivers
+ if (receiverDead) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
}
@@ -1681,162 +1372,71 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ // remove dead records and receivers outside the loop
if (deadReceivers != null) {
- for (int i=deadReceivers.size()-1; i>=0; i--) {
- removeUpdatesLocked(deadReceivers.get(i));
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
+ }
+ }
+ if (deadUpdateRecords != null) {
+ for (UpdateRecord r : deadUpdateRecords) {
+ r.disposeLocked(true);
}
}
}
private class LocationWorkerHandler extends Handler {
-
@Override
public void handleMessage(Message msg) {
- try {
- if (msg.what == MESSAGE_LOCATION_CHANGED) {
- // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
-
- synchronized (mLock) {
- Location location = (Location) msg.obj;
- String provider = location.getProvider();
- boolean passive = (msg.arg1 == 1);
-
- if (!passive) {
- // notify other providers of the new location
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- if (!provider.equals(p.getName())) {
- p.updateLocation(location);
- }
- }
- }
+ switch (msg.what) {
+ case MSG_LOCATION_CHANGED:
+ handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
+ break;
+ }
+ }
+ }
- if (isAllowedBySettingsLocked(provider)) {
- handleLocationChangedLocked(location, passive);
- }
- }
- } else if (msg.what == MESSAGE_PACKAGE_UPDATED) {
- String packageName = (String) msg.obj;
-
- // reconnect to external providers if there is a better package
- if (mNetworkLocationProviderPackageName != null &&
- mPackageManager.resolveService(
- new Intent(LocationProviderProxy.SERVICE_ACTION)
- .setPackage(packageName), 0) != null) {
- // package implements service, perform full check
- String bestPackage = findBestPackage(
- LocationProviderProxy.SERVICE_ACTION,
- mNetworkLocationProviderPackageName);
- if (packageName.equals(bestPackage)) {
- mNetworkLocationProvider.reconnect(bestPackage);
- mNetworkLocationProviderPackageName = packageName;
- }
- }
- if (mGeocodeProviderPackageName != null &&
- mPackageManager.resolveService(
- new Intent(GeocoderProxy.SERVICE_ACTION)
- .setPackage(packageName), 0) != null) {
- // package implements service, perform full check
- String bestPackage = findBestPackage(
- GeocoderProxy.SERVICE_ACTION,
- mGeocodeProviderPackageName);
- if (packageName.equals(bestPackage)) {
- mGeocodeProvider.reconnect(bestPackage);
- mGeocodeProviderPackageName = packageName;
- }
- }
- }
- } catch (Exception e) {
- // Log, don't crash!
- Slog.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
+ private void handleLocationChanged(Location location, boolean passive) {
+ String provider = location.getProvider();
+
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(location);
+ }
+
+ synchronized (mLock) {
+ if (isAllowedBySettingsLocked(provider)) {
+ handleLocationChangedLocked(location, passive);
}
}
}
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART);
- if (queryRestart
- || action.equals(Intent.ACTION_PACKAGE_REMOVED)
- || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
- || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
- synchronized (mLock) {
- int uidList[] = null;
- if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
- uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
- } else {
- uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)};
- }
- if (uidList == null || uidList.length == 0) {
- return;
- }
- for (int uid : uidList) {
- if (uid >= 0) {
- ArrayList<Receiver> removedRecs = null;
- for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
- for (int j=i.size()-1; j>=0; j--) {
- UpdateRecord ur = i.get(j);
- if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
- if (queryRestart) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
- if (removedRecs == null) {
- removedRecs = new ArrayList<Receiver>();
- }
- if (!removedRecs.contains(ur.mReceiver)) {
- removedRecs.add(ur.mReceiver);
- }
- }
- }
- }
+ public void onPackageDisappeared(String packageName, int reason) {
+ // remove all receivers associated with this package name
+ synchronized (mLock) {
+ ArrayList<Receiver> deadReceivers = null;
+
+ for (Receiver receiver : mReceivers.values()) {
+ if (receiver.mPackageName.equals(packageName)) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<Receiver>();
}
+ deadReceivers.add(receiver);
}
}
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- boolean noConnectivity =
- intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- if (!noConnectivity) {
- mNetworkState = LocationProvider.AVAILABLE;
- } else {
- mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
- }
-
- final NetworkInfo info = intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- // Notify location providers of current network state
- synchronized (mLock) {
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface provider = mProviders.get(i);
- if (provider.requiresNetwork()) {
- provider.updateNetworkState(mNetworkState, info);
- }
+ // perform removal outside of mReceivers loop
+ if (deadReceivers != null) {
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
}
}
}
}
};
- private final PackageMonitor mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- // Called by main thread; divert work to LocationWorker.
- Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
- }
- @Override
- public void onPackageAdded(String packageName, int uid) {
- // Called by main thread; divert work to LocationWorker.
- Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
- }
- @Override
- public void onPackageDisappeared(String packageName, int uid) {
- mGeofenceManager.removeFence(packageName);
- }
- };
-
// Wake locks
private void incrementPendingBroadcasts() {
@@ -1922,9 +1522,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
@Override
- public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ public void addTestProvider(String name, ProviderProperties properties) {
checkMockPermissionsSafe();
if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
@@ -1933,25 +1531,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
long identity = Binder.clearCallingIdentity();
synchronized (mLock) {
- MockProvider provider = new MockProvider(name, this,
- requiresNetwork, requiresSatellite,
- requiresCell, hasMonetaryCost, supportsAltitude,
- supportsSpeed, supportsBearing, powerRequirement, accuracy);
+ MockProvider provider = new MockProvider(name, this, properties);
// remove the real provider if we are replacing GPS or network provider
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)) {
LocationProviderInterface p = mProvidersByName.get(name);
if (p != null) {
- p.enableLocationTracking(false);
- removeProvider(p);
+ removeProviderLocked(p);
}
}
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
- addProvider(provider);
+ addProviderLocked(provider);
mMockProviders.put(name, provider);
- mLastKnownLocation.put(name, null);
+ mLastLocation.put(name, null);
updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
@@ -1966,17 +1560,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
long identity = Binder.clearCallingIdentity();
- removeProvider(mProvidersByName.get(provider));
+ removeProviderLocked(mProvidersByName.get(provider));
mMockProviders.remove(mockProvider);
- // reinstall real provider if we were mocking GPS or network provider
- if (LocationManager.GPS_PROVIDER.equals(provider) &&
- mGpsLocationProvider != null) {
- addProvider(mGpsLocationProvider);
- } else if (LocationManager.NETWORK_PROVIDER.equals(provider) &&
- mNetworkLocationProvider != null) {
- addProvider(mNetworkLocationProvider);
- }
- mLastKnownLocation.put(provider, null);
+
+ // reinstate real provider if available
+ LocationProviderInterface realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
updateProvidersLocked();
Binder.restoreCallingIdentity(identity);
}
@@ -2072,6 +1664,106 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ private static double wrapLatitude(double lat) {
+ if (lat > MAX_LATITUDE) lat = MAX_LATITUDE;
+ if (lat < -MAX_LATITUDE) lat = -MAX_LATITUDE;
+ return lat;
+ }
+
+ private static double wrapLongitude(double lon) {
+ if (lon >= 180.0) lon -= 360.0;
+ if (lon < -180.0) lon += 360.0;
+ return lon;
+ }
+
+ private static double distanceToDegreesLatitude(double distance) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
+ }
+
+ /**
+ * Requires latitude since longitudinal distances change with distance from equator.
+ */
+ private static double distanceToDegreesLongitude(double distance, double lat) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(lat);
+ }
+
+ /**
+ * Fudge a location into a coarse location.
+ * <p>Add a random offset, then quantize the result (snap-to-grid).
+ * Random offsets alone can be low-passed pretty easily.
+ * Snap-to-grid on its own is excellent unless you are sitting on a
+ * grid boundary and bouncing between quantizations.
+ * The combination is quite hard to reverse engineer.
+ * <p>The random offset used is smaller than the goal accuracy
+ * ({@link #COARSE_ACCURACY_M}), in order to give relatively stable
+ * results after quantization.
+ */
+ private static Location createCoarse(Location fine) {
+ Location coarse = new Location(fine);
+
+ coarse.removeBearing();
+ coarse.removeSpeed();
+ coarse.removeAltitude();
+
+ double lat = coarse.getLatitude();
+ double lon = coarse.getLongitude();
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ if (coarse.getAccuracy() < COARSE_ACCURACY_M / 2) {
+ // apply a random offset
+ double fudgeDistance = COARSE_ACCURACY_M / 2.0 - coarse.getAccuracy();
+ lat += (Math.random() - 0.5) * distanceToDegreesLatitude(fudgeDistance);
+ lon += (Math.random() - 0.5) * distanceToDegreesLongitude(fudgeDistance, lat);
+ }
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // quantize (snap-to-grid)
+ double latGranularity = distanceToDegreesLatitude(COARSE_ACCURACY_M);
+ double lonGranularity = distanceToDegreesLongitude(COARSE_ACCURACY_M, lat);
+ long latQuantized = Math.round(lat / latGranularity);
+ long lonQuantized = Math.round(lon / lonGranularity);
+ lat = latQuantized * latGranularity;
+ lon = lonQuantized * lonGranularity;
+
+ // wrap again
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // apply
+ coarse.setLatitude(lat);
+ coarse.setLongitude(lon);
+ coarse.setAccuracy((float)COARSE_ACCURACY_M);
+
+ return coarse;
+ }
+
+
+ private static Location getCoarseLocationExtra(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) return null;
+ Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
+ if (parcel == null) return null;
+ if (!(parcel instanceof Location)) return null;
+ Location coarse = (Location) parcel;
+ if (coarse.getAccuracy() < COARSE_ACCURACY_M) return null;
+ return coarse;
+ }
+
+ private static Location addCoarseLocationExtra(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) extras = new Bundle();
+ Location coarse = createCoarse(location);
+ extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
+ location.setExtras(extras);
+ return coarse;
+ }
+
private void log(String log) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, log);
@@ -2090,36 +1782,26 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (mLock) {
pw.println("Current Location Manager state:");
- pw.println(" sProvidersLoaded=" + sProvidersLoaded);
- pw.println(" Listeners:");
- int N = mReceivers.size();
- for (int i=0; i<N; i++) {
- pw.println(" " + mReceivers.get(i));
- }
pw.println(" Location Listeners:");
- for (Receiver i : mReceivers.values()) {
- pw.println(" " + i + ":");
- for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
- pw.println(" " + j.getKey() + ":");
- j.getValue().dump(pw, " ");
- }
+ for (Receiver receiver : mReceivers.values()) {
+ pw.println(" " + receiver);
}
pw.println(" Records by Provider:");
- for (Map.Entry<String, ArrayList<UpdateRecord>> i
- : mRecordsByProvider.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- for (UpdateRecord j : i.getValue()) {
- pw.println(" " + j + ":");
- j.dump(pw, " ");
+ for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
+ pw.println(" " + entry.getKey() + ":");
+ for (UpdateRecord record : entry.getValue()) {
+ pw.println(" " + record);
}
}
pw.println(" Last Known Locations:");
- for (Map.Entry<String, Location> i
- : mLastKnownLocation.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- i.getValue().dump(new PrintWriterPrinter(pw), " ");
+ for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
+ String provider = entry.getKey();
+ Location location = entry.getValue();
+ pw.println(" " + provider + ": " + location);
}
+
mGeofenceManager.dump(pw);
+
if (mEnabledProviders.size() > 0) {
pw.println(" Enabled Providers:");
for (String i : mEnabledProviders) {
@@ -2140,12 +1822,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
i.getValue().dump(pw, " ");
}
}
+
+ if (args.length > 0 && "short".equals(args[0])) {
+ return;
+ }
for (LocationProviderInterface provider: mProviders) {
- String state = provider.getInternalState();
- if (state != null) {
- pw.println(provider.getName() + " Internal State:");
- pw.write(state);
+ pw.print(provider.getName() + " Internal State");
+ if (provider instanceof LocationProviderProxy) {
+ LocationProviderProxy proxy = (LocationProviderProxy) provider;
+ pw.print(" (" + proxy.getConnectedPackageName() + ")");
}
+ pw.println(":");
+ provider.dump(fd, pw, args);
}
}
}
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
new file mode 100644
index 0000000..0dfaa05
--- /dev/null
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -0,0 +1,274 @@
+/*
+ * 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.server;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.content.PackageMonitor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Find the best Service, and bind to it.
+ * Handles run-time package changes.
+ */
+public class ServiceWatcher implements ServiceConnection {
+ private static final boolean D = false;
+ private static final String EXTRA_VERSION = "version";
+
+ private final String mTag;
+ private final Context mContext;
+ private final PackageManager mPm;
+ private final List<HashSet<Signature>> mSignatureSets;
+ private final String mAction;
+ private final Runnable mNewServiceWork;
+ private final Handler mHandler;
+
+ private Object mLock = new Object();
+
+ // all fields below synchronized on mLock
+ private IBinder mBinder; // connected service
+ private String mPackageName; // current best package
+ private int mVersion; // current best version
+
+ public ServiceWatcher(Context context, String logTag, String action,
+ List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
+ mContext = context;
+ mTag = logTag;
+ mAction = action;
+ mPm = mContext.getPackageManager();
+ mNewServiceWork = newServiceWork;
+ mHandler = handler;
+
+ mSignatureSets = new ArrayList<HashSet<Signature>>();
+ for (int i=0; i < initialPackageNames.size(); i++) {
+ String pkg = initialPackageNames.get(i);
+ HashSet<Signature> set = new HashSet<Signature>();
+ try {
+ Signature[] sigs =
+ mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
+ set.addAll(Arrays.asList(sigs));
+ mSignatureSets.add(set);
+ } catch (NameNotFoundException e) {
+ Log.w(logTag, pkg + " not found");
+ }
+ }
+
+ }
+
+ public boolean start() {
+ if (!bindBestPackage(null)) return false;
+
+ mPackageMonitor.register(mContext, null, true);
+ return true;
+ }
+
+ /**
+ * Searches and binds to the best package, or do nothing
+ * if the best package is already bound.
+ * Only checks the named package, or checks all packages if it
+ * is null.
+ * Return true if a new package was found to bind to.
+ */
+ private boolean bindBestPackage(String justCheckThisPackage) {
+ Intent intent = new Intent(mAction);
+ if (justCheckThisPackage != null) {
+ intent.setPackage(justCheckThisPackage);
+ }
+ List<ResolveInfo> rInfos = mPm.queryIntentServices(new Intent(mAction),
+ PackageManager.GET_META_DATA);
+ int bestVersion = Integer.MIN_VALUE;
+ String bestPackage = null;
+ for (ResolveInfo rInfo : rInfos) {
+ String packageName = rInfo.serviceInfo.packageName;
+
+ // check signature
+ try {
+ PackageInfo pInfo;
+ pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ if (!isSignatureMatch(pInfo.signatures)) {
+ Log.w(mTag, packageName + " resolves service " + mAction +
+ ", but has wrong signature, ignoring");
+ continue;
+ }
+ } catch (NameNotFoundException e) {
+ Log.wtf(mTag, e);
+ continue;
+ }
+
+ // check version
+ int version = 0;
+ if (rInfo.serviceInfo.metaData != null) {
+ version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0);
+ }
+ if (version > mVersion) {
+ bestVersion = version;
+ bestPackage = packageName;
+ }
+ }
+
+ if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s",
+ (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
+ rInfos.size(),
+ (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
+
+ if (bestPackage != null) {
+ bindToPackage(bestPackage, bestVersion);
+ return true;
+ }
+ return false;
+ }
+
+ private void unbind() {
+ String pkg;
+ synchronized (mLock) {
+ pkg = mPackageName;
+ mPackageName = null;
+ mVersion = Integer.MIN_VALUE;
+ }
+ if (pkg != null) {
+ if (D) Log.d(mTag, "unbinding " + pkg);
+ mContext.unbindService(this);
+ }
+ }
+
+ private void bindToPackage(String packageName, int version) {
+ unbind();
+ Intent intent = new Intent(mAction);
+ intent.setPackage(packageName);
+ synchronized (mLock) {
+ mPackageName = packageName;
+ mVersion = version;
+ }
+ if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
+ mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ }
+
+ private boolean isSignatureMatch(Signature[] signatures) {
+ if (signatures == null) return false;
+
+ // build hashset of input to test against
+ HashSet<Signature> inputSet = new HashSet<Signature>();
+ for (Signature s : signatures) {
+ inputSet.add(s);
+ }
+
+ // test input against each of the signature sets
+ for (HashSet<Signature> referenceSet : mSignatureSets) {
+ if (referenceSet.equals(inputSet)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ /**
+ * Called when package has been reinstalled
+ */
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbind();
+ }
+ // check the updated package in case it is better
+ bindBestPackage(packageName);
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbind();
+ }
+ // check the new package is case it is better
+ bindBestPackage(packageName);
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ unbind();
+ // the currently bound package was removed,
+ // need to search for a new package
+ bindBestPackage(null);
+ }
+ }
+ };
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ synchronized (mLock) {
+ String packageName = name.getPackageName();
+ if (packageName.equals(mPackageName)) {
+ if (D) Log.d(mTag, packageName + " connected");
+ mBinder = binder;
+ if (mHandler !=null && mNewServiceWork != null) {
+ mHandler.post(mNewServiceWork);
+ }
+ } else {
+ Log.w(mTag, "unexpected onServiceConnected: " + packageName);
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ String packageName = name.getPackageName();
+ if (D) Log.d(mTag, packageName + " disconnected");
+
+ if (packageName.equals(mPackageName)) {
+ mBinder = null;
+ }
+ }
+ }
+
+ public String getBestPackageName() {
+ synchronized (mLock) {
+ return mPackageName;
+ }
+ }
+
+ public int getBestVersion() {
+ synchronized (mLock) {
+ return mVersion;
+ }
+ }
+
+ public IBinder getBinder() {
+ synchronized (mLock) {
+ return mBinder;
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 07f3125..7d030e9 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -16,92 +16,64 @@
package com.android.server.location;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.location.Address;
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
-import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
+import com.android.server.ServiceWatcher;
import java.util.List;
/**
- * A class for proxying IGeocodeProvider implementations.
- *
- * {@hide}
+ * Proxy for IGeocodeProvider implementations.
*/
public class GeocoderProxy {
-
private static final String TAG = "GeocoderProxy";
- public static final String SERVICE_ACTION =
- "com.android.location.service.GeocodeProvider";
+ private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
private final Context mContext;
- private final Intent mIntent;
- private final Object mMutex = new Object(); // synchronizes access to mServiceConnection
- private Connection mServiceConnection; // never null after ctor
-
- public GeocoderProxy(Context context, String packageName) {
- mContext = context;
- mIntent = new Intent(SERVICE_ACTION);
- reconnect(packageName);
- }
-
- /** Bind to service. Will reconnect if already connected */
- public void reconnect(String packageName) {
- synchronized (mMutex) {
- if (mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- }
- mServiceConnection = new Connection();
- mIntent.setPackage(packageName);
- mContext.bindService(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ private final ServiceWatcher mServiceWatcher;
+
+ public static GeocoderProxy createAndBind(Context context,
+ List<String> initialPackageNames) {
+ GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames);
+ if (proxy.bind()) {
+ return proxy;
+ } else {
+ return null;
}
}
- private class Connection implements ServiceConnection {
+ public GeocoderProxy(Context context, List<String> initialPackageNames) {
+ mContext = context;
- private IGeocodeProvider mProvider;
+ mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
+ null, null);
+ }
- public void onServiceConnected(ComponentName className, IBinder service) {
- synchronized (this) {
- mProvider = IGeocodeProvider.Stub.asInterface(service);
- }
- }
+ private boolean bind () {
+ return mServiceWatcher.start();
+ }
- public void onServiceDisconnected(ComponentName className) {
- synchronized (this) {
- mProvider = null;
- }
- }
+ private IGeocodeProvider getService() {
+ return IGeocodeProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
- public IGeocodeProvider getProvider() {
- synchronized (this) {
- return mProvider;
- }
- }
+ public String getConnectedPackageName() {
+ return mServiceWatcher.getBestPackageName();
}
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
+ IGeocodeProvider provider = getService();
if (provider != null) {
try {
- return provider.getFromLocation(latitude, longitude, maxResults,
- params, addrs);
+ return provider.getFromLocation(latitude, longitude, maxResults, params, addrs);
} catch (RemoteException e) {
- Log.e(TAG, "getFromLocation failed", e);
+ Log.w(TAG, e);
}
}
return "Service not Available";
@@ -111,19 +83,17 @@ public class GeocoderProxy {
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
+ IGeocodeProvider provider = getService();
if (provider != null) {
try {
return provider.getFromLocationName(locationName, lowerLeftLatitude,
lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
maxResults, params, addrs);
} catch (RemoteException e) {
- Log.e(TAG, "getFromLocationName failed", e);
+ Log.w(TAG, e);
}
}
return "Service not Available";
}
+
}
diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java
index b3f53ea..338cd5d 100644
--- a/services/java/com/android/server/location/GeofenceManager.java
+++ b/services/java/com/android/server/location/GeofenceManager.java
@@ -25,47 +25,36 @@ import android.Manifest.permission;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.location.Geofence;
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.PowerManager;
import android.os.SystemClock;
public class GeofenceManager implements LocationListener, PendingIntent.OnFinished {
- static final String TAG = "GeofenceManager";
+ private static final String TAG = "GeofenceManager";
/**
* Assume a maximum land speed, as a heuristic to throttle location updates.
* (Air travel should result in an airplane mode toggle which will
* force a new location update anyway).
*/
- static final int MAX_SPEED_M_S = 100; // 360 km/hr (high speed train)
-
- class GeofenceWrapper {
- final Geofence fence;
- final long expiry;
- final String packageName;
- final PendingIntent intent;
-
- public GeofenceWrapper(Geofence fence, long expiry, String packageName,
- PendingIntent intent) {
- this.fence = fence;
- this.expiry = expiry;
- this.packageName = packageName;
- this.intent = intent;
- }
- }
+ private static final int MAX_SPEED_M_S = 100; // 360 km/hr (high speed train)
- final Context mContext;
- final LocationManager mLocationManager;
- final PowerManager.WakeLock mWakeLock;
- final Looper mLooper; // looper thread to take location updates on
+ private final Context mContext;
+ private final LocationManager mLocationManager;
+ private final PowerManager.WakeLock mWakeLock;
+ private final Looper mLooper; // looper thread to take location updates on
- // access to members below is synchronized on this
- Location mLastLocation;
- List<GeofenceWrapper> mFences = new LinkedList<GeofenceWrapper>();
+ private Object mLock = new Object();
+
+ // access to members below is synchronized on mLock
+ private Location mLastLocation;
+ private List<GeofenceState> mFences = new LinkedList<GeofenceState>();
public GeofenceManager(Context context) {
mContext = context;
@@ -73,82 +62,98 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mLooper = Looper.myLooper();
- mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);
- }
- public void addFence(double latitude, double longitude, float radius, long expiration,
- PendingIntent intent, int uid, String packageName) {
- long expiry = SystemClock.elapsedRealtime() + expiration;
- if (expiration < 0) {
- expiry = Long.MAX_VALUE;
- }
- Geofence fence = new Geofence(latitude, longitude, radius, mLastLocation);
- GeofenceWrapper fenceWrapper = new GeofenceWrapper(fence, expiry, packageName, intent);
-
- synchronized (this) {
- mFences.add(fenceWrapper);
- updateProviderRequirements();
+ LocationRequest request = new LocationRequest()
+ .setQuality(LocationRequest.POWER_NONE)
+ .setFastestInterval(0);
+ mLocationManager.requestLocationUpdates(request, this, Looper.myLooper());
+ }
+
+ public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, int uid,
+ String packageName) {
+ GeofenceState state = new GeofenceState(geofence, mLastLocation,
+ request.getExpireAt(), packageName, intent);
+
+ synchronized (mLock) {
+ // first make sure it doesn't already exist
+ for (int i = mFences.size() - 1; i >= 0; i--) {
+ GeofenceState w = mFences.get(i);
+ if (geofence.equals(w.mFence) && intent.equals(w.mIntent)) {
+ // already exists, remove the old one
+ mFences.remove(i);
+ break;
+ }
+ }
+ mFences.add(state);
+ updateProviderRequirementsLocked();
}
}
- public void removeFence(PendingIntent intent) {
- synchronized (this) {
- Iterator<GeofenceWrapper> iter = mFences.iterator();
+ public void removeFence(Geofence fence, PendingIntent intent) {
+ synchronized (mLock) {
+ Iterator<GeofenceState> iter = mFences.iterator();
while (iter.hasNext()) {
- GeofenceWrapper fenceWrapper = iter.next();
- if (fenceWrapper.intent.equals(intent)) {
- iter.remove();
+ GeofenceState state = iter.next();
+ if (state.mIntent.equals(intent)) {
+
+ if (fence == null) {
+ // alwaus remove
+ iter.remove();
+ } else {
+ // just remove matching fences
+ if (fence.equals(state.mFence)) {
+ iter.remove();
+ }
+ }
}
}
- updateProviderRequirements();
+ updateProviderRequirementsLocked();
}
}
public void removeFence(String packageName) {
- synchronized (this) {
- Iterator<GeofenceWrapper> iter = mFences.iterator();
+ synchronized (mLock) {
+ Iterator<GeofenceState> iter = mFences.iterator();
while (iter.hasNext()) {
- GeofenceWrapper fenceWrapper = iter.next();
- if (fenceWrapper.packageName.equals(packageName)) {
+ GeofenceState state = iter.next();
+ if (state.mPackageName.equals(packageName)) {
iter.remove();
}
}
- updateProviderRequirements();
+ updateProviderRequirementsLocked();
}
}
- void removeExpiredFences() {
- synchronized (this) {
- long time = SystemClock.elapsedRealtime();
- Iterator<GeofenceWrapper> iter = mFences.iterator();
- while (iter.hasNext()) {
- GeofenceWrapper fenceWrapper = iter.next();
- if (fenceWrapper.expiry < time) {
- iter.remove();
- }
+ private void removeExpiredFencesLocked() {
+ long time = SystemClock.elapsedRealtime();
+ Iterator<GeofenceState> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceState state = iter.next();
+ if (state.mExpireAt < time) {
+ iter.remove();
}
}
}
- void processLocation(Location location) {
+ private void processLocation(Location location) {
List<PendingIntent> enterIntents = new LinkedList<PendingIntent>();
List<PendingIntent> exitIntents = new LinkedList<PendingIntent>();
- synchronized (this) {
+ synchronized (mLock) {
mLastLocation = location;
- removeExpiredFences();
+ removeExpiredFencesLocked();
- for (GeofenceWrapper fenceWrapper : mFences) {
- int event = fenceWrapper.fence.processLocation(location);
- if ((event & Geofence.FLAG_ENTER) != 0) {
- enterIntents.add(fenceWrapper.intent);
+ for (GeofenceState state : mFences) {
+ int event = state.processLocation(location);
+ if ((event & GeofenceState.FLAG_ENTER) != 0) {
+ enterIntents.add(state.mIntent);
}
- if ((event & Geofence.FLAG_EXIT) != 0) {
- exitIntents.add(fenceWrapper.intent);
+ if ((event & GeofenceState.FLAG_EXIT) != 0) {
+ exitIntents.add(state.mIntent);
}
}
- updateProviderRequirements();
+ updateProviderRequirementsLocked();
}
// release lock before sending intents
@@ -160,52 +165,50 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
}
}
- void sendIntentEnter(PendingIntent pendingIntent) {
+ private void sendIntentEnter(PendingIntent pendingIntent) {
Intent intent = new Intent();
intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
sendIntent(pendingIntent, intent);
}
- void sendIntentExit(PendingIntent pendingIntent) {
+ private void sendIntentExit(PendingIntent pendingIntent) {
Intent intent = new Intent();
intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
sendIntent(pendingIntent, intent);
}
- void sendIntent(PendingIntent pendingIntent, Intent intent) {
+ private void sendIntent(PendingIntent pendingIntent, Intent intent) {
try {
mWakeLock.acquire();
pendingIntent.send(mContext, 0, intent, this, null, permission.ACCESS_FINE_LOCATION);
} catch (PendingIntent.CanceledException e) {
- removeFence(pendingIntent);
+ removeFence(null, pendingIntent);
mWakeLock.release();
}
}
- void updateProviderRequirements() {
- synchronized (this) {
- double minDistance = Double.MAX_VALUE;
- for (GeofenceWrapper alert : mFences) {
- if (alert.fence.getDistance() < minDistance) {
- minDistance = alert.fence.getDistance();
- }
+ private void updateProviderRequirementsLocked() {
+ double minDistance = Double.MAX_VALUE;
+ for (GeofenceState state : mFences) {
+ if (state.getDistance() < minDistance) {
+ minDistance = state.getDistance();
}
+ }
- if (minDistance == Double.MAX_VALUE) {
- disableLocation();
- } else {
- int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S;
- setLocationInterval(intervalMs);
- }
+ if (minDistance == Double.MAX_VALUE) {
+ disableLocationLocked();
+ } else {
+ int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S;
+ requestLocationLocked(intervalMs);
}
}
- void setLocationInterval(int intervalMs) {
- mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, intervalMs, 0, this,
+ private void requestLocationLocked(int intervalMs) {
+ mLocationManager.requestLocationUpdates(new LocationRequest().setInterval(intervalMs), this,
mLooper);
}
- void disableLocation() {
+ private void disableLocationLocked() {
mLocationManager.removeUpdates(this);
}
@@ -231,11 +234,12 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
public void dump(PrintWriter pw) {
pw.println(" Geofences:");
- for (GeofenceWrapper fenceWrapper : mFences) {
+
+ for (GeofenceState state : mFences) {
pw.append(" ");
- pw.append(fenceWrapper.packageName);
+ pw.append(state.mPackageName);
pw.append(" ");
- pw.append(fenceWrapper.fence.toString());
+ pw.append(state.mFence.toString());
pw.append("\n");
}
}
diff --git a/services/java/com/android/server/location/Geofence.java b/services/java/com/android/server/location/GeofenceState.java
index f63607a..1fd737f 100644
--- a/services/java/com/android/server/location/Geofence.java
+++ b/services/java/com/android/server/location/GeofenceState.java
@@ -17,37 +17,42 @@
package com.android.server.location;
+import android.app.PendingIntent;
+import android.location.Geofence;
import android.location.Location;
/**
- * Represents a simple circular GeoFence.
+ * Represents state associated with a geofence
*/
-public class Geofence {
+public class GeofenceState {
public final static int FLAG_ENTER = 0x01;
public final static int FLAG_EXIT = 0x02;
- static final int STATE_UNKNOWN = 0;
- static final int STATE_INSIDE = 1;
- static final int STATE_OUTSIDE = 2;
+ private static final int STATE_UNKNOWN = 0;
+ private static final int STATE_INSIDE = 1;
+ private static final int STATE_OUTSIDE = 2;
- final double mLatitude;
- final double mLongitude;
- final float mRadius;
- final Location mLocation;
+ public final Geofence mFence;
+ private final Location mLocation;
+ public final long mExpireAt;
+ public final String mPackageName;
+ public final PendingIntent mIntent;
int mState; // current state
double mDistance; // current distance to center of fence
- public Geofence(double latitude, double longitude, float radius, Location prevLocation) {
+ public GeofenceState(Geofence fence, Location prevLocation, long expireAt,
+ String packageName, PendingIntent intent) {
mState = STATE_UNKNOWN;
- mLatitude = latitude;
- mLongitude = longitude;
- mRadius = radius;
+ mFence = fence;
+ mExpireAt = expireAt;
+ mPackageName = packageName;
+ mIntent = intent;
mLocation = new Location("");
- mLocation.setLatitude(latitude);
- mLocation.setLongitude(longitude);
+ mLocation.setLatitude(fence.getLatitude());
+ mLocation.setLongitude(fence.getLongitude());
if (prevLocation != null) {
processLocation(prevLocation);
@@ -63,7 +68,7 @@ public class Geofence {
int prevState = mState;
//TODO: inside/outside detection could be made more rigorous
- boolean inside = mDistance <= Math.max(mRadius, location.getAccuracy());
+ boolean inside = mDistance <= Math.max(mFence.getRadius(), location.getAccuracy());
if (inside) {
mState = STATE_INSIDE;
} else {
@@ -94,7 +99,6 @@ public class Geofence {
default:
state = "?";
}
- return String.format("(%.4f, %.4f r=%.0f d=%.0f %s)", mLatitude, mLongitude, mRadius,
- mDistance, state);
+ return String.format("%s d=%.0f %s", mFence.toString(), mDistance, state);
}
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index bd7668b..3cd767d 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -29,6 +29,7 @@ import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
+import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.ConnectivityManager;
@@ -54,17 +55,19 @@ import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.util.NtpTrustedTime;
-import android.util.SparseIntArray;
-
import com.android.internal.app.IBatteryStats;
import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Date;
@@ -84,6 +87,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final ProviderProperties PROPERTIES = new ProviderProperties(
+ true, true, false, false, true, true, true,
+ Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
+
// these need to match GpsPositionMode enum in gps.h
private static final int GPS_POSITION_MODE_STANDALONE = 0;
private static final int GPS_POSITION_MODE_MS_BASED = 1;
@@ -150,14 +157,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Handler messages
private static final int CHECK_LOCATION = 1;
private static final int ENABLE = 2;
- private static final int ENABLE_TRACKING = 3;
+ private static final int SET_REQUEST = 3;
private static final int UPDATE_NETWORK_STATE = 4;
private static final int INJECT_NTP_TIME = 5;
private static final int DOWNLOAD_XTRA_DATA = 6;
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;
// Request setid
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -179,6 +185,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final String PROPERTIES_FILE = "/etc/gps.conf";
+ /** simpler wrapper for ProviderRequest + Worksource */
+ private static class GpsRequest {
+ public ProviderRequest request;
+ public WorkSource source;
+ public GpsRequest(ProviderRequest request, WorkSource source) {
+ this.request = request;
+ this.source = source;
+ }
+ }
+
+ private Object mLock = new Object();
+
private int mLocationFlags = LOCATION_INVALID;
// current status
@@ -198,9 +216,16 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Typical hot TTTF is ~5 seconds, so 10 seconds seems sane.
private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
- // true if we are enabled
- private volatile boolean mEnabled;
-
+ // how often to request NTP time, in milliseconds
+ // current setting 24 hours
+ private static final long NTP_INTERVAL = 24*60*60*1000;
+ // how long to wait if we have a network error in NTP or XTRA downloading
+ // current setting - 5 minutes
+ private static final long RETRY_INTERVAL = 5*60*1000;
+
+ // true if we are enabled, protected by this
+ private boolean mEnabled;
+
// true if we have network connectivity
private boolean mNetworkAvailable;
@@ -217,16 +242,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
// true if GPS engine is on
private boolean mEngineOn;
-
+
// requested frequency of fixes, in milliseconds
private int mFixInterval = 1000;
// 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;
@@ -236,7 +258,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// for calculating time to first fix
private long mFixRequestTime = 0;
// time to first fix for most recent session
- private int mTTFF = 0;
+ private int mTimeToFirstFix = 0;
// time we received our last fix
private long mLastFixTime;
@@ -251,7 +273,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final Context mContext;
private final NtpTrustedTime mNtpTime;
- private final ILocationManager mLocationManager;
+ private final ILocationManager mILocationManager;
private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle();
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
@@ -267,17 +289,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
private int mAGpsDataConnectionState;
private int mAGpsDataConnectionIpAddr;
private final ConnectivityManager mConnMgr;
- private final GpsNetInitiatedHandler mNIHandler;
+ private final GpsNetInitiatedHandler mNIHandler;
// Wakelocks
private final static String WAKELOCK_KEY = "GpsLocationProvider";
private final PowerManager.WakeLock mWakeLock;
- // bitfield of pending messages to our Handler
- // used only for messages that cannot have multiple instances queued
- private int mPendingMessageBits;
- // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
- // which might have multiple instances queued
- private int mPendingListenerMessages;
// Alarms
private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
@@ -287,22 +303,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final PendingIntent mTimeoutIntent;
private final IBatteryStats mBatteryStats;
- private final SparseIntArray mClientUids = new SparseIntArray();
- // how often to request NTP time, in milliseconds
- // current setting 24 hours
- private static final long NTP_INTERVAL = 24*60*60*1000;
- // how long to wait if we have a network error in NTP or XTRA downloading
- // current setting - 5 minutes
- private static final long RETRY_INTERVAL = 5*60*1000;
+ // only modified on handler thread
+ private int[] mClientUids = new int[0];
private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
+ @Override
public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
if (listener == null) {
throw new NullPointerException("listener is null in addGpsStatusListener");
}
- synchronized(mListeners) {
+ synchronized (mListeners) {
IBinder binder = listener.asBinder();
int size = mListeners.size();
for (int i = 0; i < size; i++) {
@@ -319,12 +331,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ @Override
public void removeGpsStatusListener(IGpsStatusListener listener) {
if (listener == null) {
throw new NullPointerException("listener is null in addGpsStatusListener");
}
- synchronized(mListeners) {
+ synchronized (mListeners) {
IBinder binder = listener.asBinder();
Listener l = null;
int size = mListeners.size();
@@ -353,7 +366,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (action.equals(ALARM_WAKEUP)) {
if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
- startNavigating(false);
+ startNavigating();
} else if (action.equals(ALARM_TIMEOUT)) {
if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
@@ -361,6 +374,22 @@ public class GpsLocationProvider implements LocationProviderInterface {
checkSmsSuplInit(intent);
} else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
checkWapSuplInit(intent);
+ } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ int networkState;
+ if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
+ networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
+ } else {
+ networkState = LocationProvider.AVAILABLE;
+ }
+
+ // retrieve NetworkInfo result for this UID
+ NetworkInfo info =
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+ ConnectivityManager connManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ info = connManager.getNetworkInfo(info.getType());
+
+ updateNetworkState(networkState, info);
}
}
};
@@ -382,10 +411,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
return native_is_supported();
}
- public GpsLocationProvider(Context context, ILocationManager locationManager) {
+ public GpsLocationProvider(Context context, ILocationManager ilocationManager) {
mContext = context;
mNtpTime = NtpTrustedTime.getInstance(context);
- mLocationManager = locationManager;
+ mILocationManager = ilocationManager;
mNIHandler = new GpsNetInitiatedHandler(context);
mLocation.setExtras(mLocationExtras);
@@ -393,7 +422,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Create a wake lock
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- mWakeLock.setReferenceCounted(false);
+ mWakeLock.setReferenceCounted(true);
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
@@ -473,16 +502,14 @@ public class GpsLocationProvider implements LocationProviderInterface {
/**
* Returns the name of this provider.
*/
+ @Override
public String getName() {
return LocationManager.GPS_PROVIDER;
}
- /**
- * Returns true if the provider requires access to a
- * data network (e.g., the Internet), false otherwise.
- */
- public boolean requiresNetwork() {
- return true;
+ @Override
+ public ProviderProperties getProperties() {
+ return PROPERTIES;
}
public void updateNetworkState(int state, NetworkInfo info) {
@@ -516,7 +543,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
String apnName = info.getExtraInfo();
if (mNetworkAvailable) {
if (apnName == null) {
- /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
+ /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
exception in the following call to native_agps_data_conn_open*/
apnName = "dummy-apn";
}
@@ -613,18 +640,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
// try again later
// since this is delayed and not urgent we do not hold a wake lock here
mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA),
+ RETRY_INTERVAL);
}
}
- /**
- * This is called to inform us when another location provider returns a location.
- * Someday we might use this for network location injection to aid the GPS
- */
- public void updateLocation(Location location) {
- sendMessage(UPDATE_LOCATION, 0, location);
- }
-
private void handleUpdateLocation(Location location) {
if (location.hasAccuracy()) {
native_inject_location(location.getLatitude(), location.getLongitude(),
@@ -633,107 +653,26 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
/**
- * Returns true if the provider requires access to a
- * satellite-based positioning system (e.g., GPS), false
- * otherwise.
- */
- public boolean requiresSatellite() {
- return true;
- }
-
- /**
- * Returns true if the provider requires access to an appropriate
- * cellular network (e.g., to make use of cell tower IDs), false
- * otherwise.
- */
- public boolean requiresCell() {
- return false;
- }
-
- /**
- * Returns true if the use of this provider may result in a
- * monetary charge to the user, false if use is free. It is up to
- * each provider to give accurate information.
- */
- public boolean hasMonetaryCost() {
- return false;
- }
-
- /**
- * Returns true if the provider is able to provide altitude
- * information, false otherwise. A provider that reports altitude
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsAltitude() {
- return true;
- }
-
- /**
- * Returns true if the provider is able to provide speed
- * information, false otherwise. A provider that reports speed
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsSpeed() {
- return true;
- }
-
- /**
- * Returns true if the provider is able to provide bearing
- * information, false otherwise. A provider that reports bearing
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsBearing() {
- return true;
- }
-
- /**
- * Returns the power requirement for this provider.
- *
- * @return the power requirement for this provider, as one of the
- * constants Criteria.POWER_REQUIREMENT_*.
- */
- public int getPowerRequirement() {
- 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
- *
- * @return the accuracy of location from this provider, as one
- * of the constants Criteria.ACCURACY_*.
- */
- public int getAccuracy() {
- return Criteria.ACCURACY_FINE;
- }
-
- /**
* Enables this provider. When enabled, calls to getStatus()
* must be handled. Hardware may be started up
* when the provider is enabled.
*/
+ @Override
public void enable() {
- synchronized (mHandler) {
- sendMessage(ENABLE, 1, null);
- }
+ sendMessage(ENABLE, 1, null);
}
private void handleEnable() {
if (DEBUG) Log.d(TAG, "handleEnable");
- if (mEnabled) return;
- mEnabled = native_init();
- if (mEnabled) {
+ synchronized (mLock) {
+ if (mEnabled) return;
+ mEnabled = true;
+ }
+
+ boolean enabled = native_init();
+
+ if (enabled) {
mSupportsXtra = native_supports_xtra();
if (mSuplServerHost != null) {
native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
@@ -742,6 +681,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
}
} else {
+ synchronized (mLock) {
+ mEnabled = false;
+ }
Log.w(TAG, "Failed to enable location provider");
}
}
@@ -751,27 +693,35 @@ public class GpsLocationProvider implements LocationProviderInterface {
* need not be handled. Hardware may be shut
* down while the provider is disabled.
*/
+ @Override
public void disable() {
- synchronized (mHandler) {
- sendMessage(ENABLE, 0, null);
- }
+ sendMessage(ENABLE, 0, null);
}
private void handleDisable() {
if (DEBUG) Log.d(TAG, "handleDisable");
- if (!mEnabled) return;
- mEnabled = false;
+ synchronized (mLock) {
+ if (!mEnabled) return;
+ mEnabled = false;
+ }
+
stopNavigating();
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
// do this before releasing wakelock
native_cleanup();
}
+ @Override
public boolean isEnabled() {
- return mEnabled;
+ synchronized (mLock) {
+ return mEnabled;
+ }
}
+ @Override
public int getStatus(Bundle extras) {
if (extras != null) {
extras.putInt("satellites", mSvCount);
@@ -788,93 +738,69 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ @Override
public long getStatusUpdateTime() {
return mStatusUpdateTime;
}
- 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);
- }
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
}
- private void handleEnableLocationTracking(boolean enable) {
- if (enable) {
- mTTFF = 0;
- mLastFixTime = 0;
- startNavigating(false);
- } else {
- if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
- mAlarmManager.cancel(mWakeupIntent);
- mAlarmManager.cancel(mTimeoutIntent);
- }
- stopNavigating();
- }
- }
+ private void handleSetRequest(ProviderRequest request, WorkSource source) {
+ if (DEBUG) Log.d(TAG, "setRequest " + request);
- 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, WorkSource ws) {
- if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
-
- if (minTime >= 0) {
- mFixInterval = (int)minTime;
+ if (request.reportLocation) {
+ // update client uids
+ int[] uids = new int[source.size()];
+ for (int i=0; i < source.size(); i++) {
+ uids[i] = source.get(i);
+ }
+ updateClientUids(uids);
+ mFixInterval = (int) request.interval;
+
+ // check for overflow
+ if (mFixInterval != request.interval) {
+ Log.w(TAG, "interval overflow: " + request.interval);
+ mFixInterval = Integer.MAX_VALUE;
+ }
+
+ // apply request to GPS engine
if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ // change period
if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
mFixInterval, 0, 0)) {
Log.e(TAG, "set_position_mode failed in setMinTime()");
}
+ } else if (!mStarted) {
+ // start GPS
+ startNavigating();
}
- }
- }
-
- public String getInternalState() {
- StringBuilder s = new StringBuilder();
- s.append(" mFixInterval=").append(mFixInterval).append("\n");
- s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
- if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
- if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
- if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
- if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
- s.append(")\n");
+ } else {
+ updateClientUids(new int[0]);
- s.append(native_get_internal_state());
- return s.toString();
+ stopNavigating();
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
}
private final class Listener implements IBinder.DeathRecipient {
final IGpsStatusListener mListener;
-
- int mSensors = 0;
-
+
Listener(IGpsStatusListener listener) {
mListener = listener;
}
-
+
+ @Override
public void binderDied() {
if (DEBUG) Log.d(TAG, "GPS status listener died");
- synchronized(mListeners) {
+ synchronized (mListeners) {
mListeners.remove(this);
}
if (mListener != null) {
@@ -883,64 +809,47 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- public void addListener(int uid) {
- synchronized (mWakeLock) {
- mPendingListenerMessages++;
- mWakeLock.acquire();
- Message m = Message.obtain(mHandler, ADD_LISTENER);
- m.arg1 = uid;
- mHandler.sendMessage(m);
- }
- }
-
- private void handleAddListener(int uid) {
- synchronized(mListeners) {
- if (mClientUids.indexOfKey(uid) >= 0) {
- // Shouldn't be here -- already have this uid.
- Log.w(TAG, "Duplicate add listener for uid " + uid);
- return;
+ private void updateClientUids(int[] uids) {
+ // Find uid's that were not previously tracked
+ for (int uid1 : uids) {
+ boolean newUid = true;
+ for (int uid2 : mClientUids) {
+ if (uid1 == uid2) {
+ newUid = false;
+ break;
+ }
}
- mClientUids.put(uid, 0);
- if (mNavigating) {
+ if (newUid) {
try {
- mBatteryStats.noteStartGps(uid);
+ mBatteryStats.noteStartGps(uid1);
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException in addListener");
+ Log.w(TAG, "RemoteException", e);
}
}
}
- }
-
- public void removeListener(int uid) {
- synchronized (mWakeLock) {
- mPendingListenerMessages++;
- mWakeLock.acquire();
- Message m = Message.obtain(mHandler, REMOVE_LISTENER);
- m.arg1 = uid;
- mHandler.sendMessage(m);
- }
- }
- private void handleRemoveListener(int uid) {
- synchronized(mListeners) {
- if (mClientUids.indexOfKey(uid) < 0) {
- // Shouldn't be here -- don't have this uid.
- Log.w(TAG, "Unneeded remove listener for uid " + uid);
- return;
+ // Find uid'd that were tracked but have now disappeared
+ for (int uid1 : mClientUids) {
+ boolean oldUid = true;
+ for (int uid2 : uids) {
+ if (uid1 == uid2) {
+ oldUid = false;
+ break;
+ }
}
- mClientUids.delete(uid);
- if (mNavigating) {
+ if (oldUid) {
try {
- mBatteryStats.noteStopGps(uid);
+ mBatteryStats.noteStopGps(uid1);
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException in removeListener");
+ Log.w(TAG, "RemoteException", e);
}
}
}
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
-
+
long identity = Binder.clearCallingIdentity();
boolean result = false;
@@ -957,7 +866,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
-
+
Binder.restoreCallingIdentity(identity);
return result;
}
@@ -992,18 +901,17 @@ public class GpsLocationProvider implements LocationProviderInterface {
return false;
}
- private void startNavigating(boolean singleShot) {
+ private void startNavigating() {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
+ mTimeToFirstFix = 0;
+ mLastFixTime = 0;
mStarted = true;
- mSingleShot = singleShot;
mPositionMode = GPS_POSITION_MODE_STANDALONE;
if (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
- if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
- mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
- } else if (hasCapability(GPS_CAPABILITY_MSB)) {
+ if (hasCapability(GPS_CAPABILITY_MSB)) {
mPositionMode = GPS_POSITION_MODE_MS_BASED;
}
}
@@ -1039,9 +947,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
mStarted = false;
- mSingleShot = false;
native_stop();
- mTTFF = 0;
+ mTimeToFirstFix = 0;
mLastFixTime = 0;
mLocationFlags = LOCATION_INVALID;
@@ -1056,8 +963,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
mAlarmManager.cancel(mTimeoutIntent);
mAlarmManager.cancel(mWakeupIntent);
long now = SystemClock.elapsedRealtime();
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent);
}
private boolean hasCapability(int capability) {
@@ -1105,7 +1011,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
mLocation.setExtras(mLocationExtras);
try {
- mLocationManager.reportLocation(mLocation, false);
+ mILocationManager.reportLocation(mLocation, false);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling reportLocation");
}
@@ -1113,17 +1019,17 @@ public class GpsLocationProvider implements LocationProviderInterface {
mLastFixTime = System.currentTimeMillis();
// report time to first fix
- if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
- mTTFF = (int)(mLastFixTime - mFixRequestTime);
- if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
+ if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
+ if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
// notify status listeners
- synchronized(mListeners) {
+ synchronized (mListeners) {
int size = mListeners.size();
for (int i = 0; i < size; i++) {
Listener listener = mListeners.get(i);
try {
- listener.mListener.onFirstFix(mTTFF);
+ listener.mListener.onFirstFix(mTimeToFirstFix);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in stopNavigating");
mListeners.remove(listener);
@@ -1134,9 +1040,6 @@ 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
@@ -1164,7 +1067,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private void reportStatus(int status) {
if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
- synchronized(mListeners) {
+ synchronized (mListeners) {
boolean wasNavigating = mNavigating;
switch (status) {
@@ -1202,20 +1105,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- try {
- // update battery stats
- for (int i=mClientUids.size() - 1; i >= 0; i--) {
- int uid = mClientUids.keyAt(i);
- if (mNavigating) {
- mBatteryStats.noteStartGps(uid);
- } else {
- mBatteryStats.noteStopGps(uid);
- }
- }
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException in reportStatus");
- }
-
// send an intent to notify that the GPS has been enabled or disabled.
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
@@ -1230,15 +1119,15 @@ public class GpsLocationProvider implements LocationProviderInterface {
private void reportSvStatus() {
int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
-
- synchronized(mListeners) {
+
+ synchronized (mListeners) {
int size = mListeners.size();
for (int i = 0; i < size; i++) {
Listener listener = mListeners.get(i);
try {
- listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
- mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
- mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
+ listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
+ mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
+ mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in reportSvInfo");
mListeners.remove(listener);
@@ -1254,7 +1143,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
" almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
for (int i = 0; i < svCount; i++) {
Log.v(TAG, "sv: " + mSvs[i] +
- " snr: " + (float)mSnrs[i]/10 +
+ " snr: " + mSnrs[i]/10 +
" elev: " + mSvElevations[i] +
" azimuth: " + mSvAzimuths[i] +
((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") +
@@ -1342,7 +1231,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
* called from native code to report NMEA data received
*/
private void reportNmea(long timestamp) {
- synchronized(mListeners) {
+ synchronized (mListeners) {
int size = mListeners.size();
if (size > 0) {
// don't bother creating the String if we have no listeners
@@ -1389,19 +1278,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
//=============================================================
private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
// Sends a response for an NI reqeust to HAL.
+ @Override
public boolean sendNiResponse(int notificationId, int userResponse)
{
// TODO Add Permission check
- StringBuilder extrasBuf = new StringBuilder();
-
if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
", response: " + userResponse);
native_send_ni_response(notificationId, userResponse);
return true;
}
};
-
+
public INetInitiatedListener getNetInitiatedListener() {
return mNetInitiatedListener;
}
@@ -1550,16 +1438,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
private void sendMessage(int message, int arg, Object obj) {
- // hold a wake lock while messages are pending
- synchronized (mWakeLock) {
- mPendingMessageBits |= (1 << message);
- mWakeLock.acquire();
- mHandler.removeMessages(message);
- Message m = Message.obtain(mHandler, message);
- m.arg1 = arg;
- m.obj = obj;
- mHandler.sendMessage(m);
- }
+ // hold a wake lock until this message is delivered
+ mWakeLock.acquire();
+ mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
}
private final class ProviderHandler extends Handler {
@@ -1574,11 +1455,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
handleDisable();
}
break;
- case ENABLE_TRACKING:
- handleEnableLocationTracking(msg.arg1 == 1);
- break;
- case REQUEST_SINGLE_SHOT:
- handleRequestSingleShot();
+ case SET_REQUEST:
+ GpsRequest gpsRequest = (GpsRequest) msg.obj;
+ handleSetRequest(gpsRequest.request, gpsRequest.source);
break;
case UPDATE_NETWORK_STATE:
handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
@@ -1594,22 +1473,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
case UPDATE_LOCATION:
handleUpdateLocation((Location)msg.obj);
break;
- case ADD_LISTENER:
- handleAddListener(msg.arg1);
- break;
- case REMOVE_LISTENER:
- handleRemoveListener(msg.arg1);
- break;
}
- // release wake lock if no messages are pending
- synchronized (mWakeLock) {
- mPendingMessageBits &= ~(1 << message);
- if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
- mPendingListenerMessages--;
- }
- if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
- mWakeLock.release();
- }
+ if (msg.arg2 == 1) {
+ // wakelock was taken for this message, release it
+ mWakeLock.release();
}
}
};
@@ -1620,17 +1487,39 @@ public class GpsLocationProvider implements LocationProviderInterface {
super("GpsLocationProvider");
}
+ @Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
initialize();
Looper.prepare();
+
+ LocationManager locManager =
+ (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
mHandler = new ProviderHandler();
// signal when we are initialized and ready to go
mInitializedLatch.countDown();
+ locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+ 0, 0, new NetworkLocationListener(), Looper.myLooper());
Looper.loop();
}
}
+ private final class NetworkLocationListener implements LocationListener {
+ @Override
+ public void onLocationChanged(Location location) {
+ // this callback happens on mHandler looper
+ if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
+ handleUpdateLocation(location);
+ }
+ }
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) { }
+ @Override
+ public void onProviderEnabled(String provider) { }
+ @Override
+ public void onProviderDisabled(String provider) { }
+ }
+
private String getSelectedApn() {
Uri uri = Uri.parse("content://telephony/carriers/preferapn");
String apn = null;
@@ -1650,6 +1539,22 @@ public class GpsLocationProvider implements LocationProviderInterface {
return apn;
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ StringBuilder s = new StringBuilder();
+ s.append(" mFixInterval=").append(mFixInterval).append("\n");
+ s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
+ if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
+ if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
+ if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
+ if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
+ s.append(")\n");
+
+ s.append(native_get_internal_state());
+ pw.append(s);
+ }
+
// for GPS SV statistics
private static final int MAX_SVS = 32;
private static final int EPHEMERIS_MASK = 0;
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 858a582..6f09232 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -16,42 +16,33 @@
package com.android.server.location;
-import android.location.Criteria;
-import android.location.Location;
-import android.net.NetworkInfo;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+
import android.os.Bundle;
import android.os.WorkSource;
/**
* Location Manager's interface for location providers.
- *
- * {@hide}
+ * @hide
*/
public interface LocationProviderInterface {
- String getName();
- boolean requiresNetwork();
- boolean requiresSatellite();
- boolean requiresCell();
- boolean hasMonetaryCost();
- boolean supportsAltitude();
- boolean supportsSpeed();
- boolean supportsBearing();
- int getPowerRequirement();
- boolean meetsCriteria(Criteria criteria);
- int getAccuracy();
- boolean isEnabled();
- void enable();
- void disable();
- 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, WorkSource ws);
- void updateNetworkState(int state, NetworkInfo info);
- void updateLocation(Location location);
- boolean sendExtraCommand(String command, Bundle extras);
- void addListener(int uid);
- void removeListener(int uid);
+ public String getName();
+
+ public void enable();
+ public void disable();
+ public boolean isEnabled();
+ public void setRequest(ProviderRequest request, WorkSource source);
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ // --- deprecated (but still supported) ---
+ public ProviderProperties getProperties();
+ public int getStatus(Bundle extras);
+ public long getStatusUpdateTime();
+ public boolean sendExtraCommand(String command, Bundle extras);
}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index a227ab6..7faf72c 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -16,458 +16,272 @@
package com.android.server.location;
-import android.content.ComponentName;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
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;
+import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
-import com.android.internal.location.DummyLocationProvider;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ProviderRequest;
+import com.android.server.LocationManagerService;
+import com.android.server.ServiceWatcher;
/**
- * A class for proxying location providers implemented as services.
- *
- * {@hide}
+ * Proxy for ILocationProvider implementations.
*/
public class LocationProviderProxy implements LocationProviderInterface {
-
private static final String TAG = "LocationProviderProxy";
-
- public static final String SERVICE_ACTION =
- "com.android.location.service.NetworkLocationProvider";
+ private static final boolean D = LocationManagerService.D;
private final Context mContext;
private final String mName;
- private final Intent mIntent;
- private final Handler mHandler;
- private final Object mMutex = new Object(); // synchronizes access to non-final members
- private Connection mServiceConnection; // never null after ctor
+ private final ServiceWatcher mServiceWatcher;
+
+ private Object mLock = new Object();
- // cached values set by the location manager
- private boolean mLocationTracking = false;
+ // cached values set by the location manager, synchronized on mLock
+ private ProviderProperties mProperties;
private boolean mEnabled = false;
- private long mMinTime = -1;
- private WorkSource mMinTimeSource = new WorkSource();
- private int mNetworkState;
- private NetworkInfo mNetworkInfo;
-
- // constructor for proxying location providers implemented in a separate service
- public LocationProviderProxy(Context context, String name, String packageName,
- Handler handler) {
+ private ProviderRequest mRequest = null;
+ private WorkSource mWorksource = new WorkSource();
+
+ public static LocationProviderProxy createAndBind(Context context, String name, String action,
+ List<String> initialPackageNames, Handler handler) {
+ LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
+ initialPackageNames, handler);
+ if (proxy.bind()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private LocationProviderProxy(Context context, String name, String action,
+ List<String> initialPackageNames, Handler handler) {
mContext = context;
mName = name;
- mIntent = new Intent(SERVICE_ACTION);
- mHandler = handler;
- reconnect(packageName);
+ mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
+ mNewServiceWork, handler);
}
- /** Bind to service. Will reconnect if already connected */
- public void reconnect(String packageName) {
- synchronized (mMutex) {
- if (mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- }
- mServiceConnection = new Connection();
- mIntent.setPackage(packageName);
- mContext.bindService(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND |
- Context.BIND_ALLOW_OOM_MANAGEMENT);
- }
+ private boolean bind () {
+ return mServiceWatcher.start();
}
- private class Connection implements ServiceConnection, Runnable {
-
- private ILocationProvider mProvider;
-
- // for caching requiresNetwork, requiresSatellite, etc.
- private DummyLocationProvider mCachedAttributes; // synchronized by mMutex
+ private ILocationProvider getService() {
+ return ILocationProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
- public void onServiceConnected(ComponentName className, IBinder service) {
- synchronized (this) {
- mProvider = ILocationProvider.Stub.asInterface(service);
- if (mProvider != null) {
- mHandler.post(this);
- }
- }
- }
+ public String getConnectedPackageName() {
+ return mServiceWatcher.getBestPackageName();
+ }
- public void onServiceDisconnected(ComponentName className) {
- synchronized (this) {
- mProvider = null;
+ /**
+ * Work to apply current state to a newly connected provider.
+ * Remember we can switch the service that implements a providers
+ * at run-time, so need to apply current state.
+ */
+ private Runnable mNewServiceWork = new Runnable() {
+ @Override
+ public void run() {
+ if (D) Log.d(TAG, "applying state to connected service");
+
+ boolean enabled;
+ ProviderProperties properties = null;
+ ProviderRequest request;
+ WorkSource source;
+ ILocationProvider service;
+ synchronized (mLock) {
+ enabled = mEnabled;
+ request = mRequest;
+ source = mWorksource;
+ service = getService();
}
- }
- public synchronized ILocationProvider getProvider() {
- return mProvider;
- }
-
- public synchronized DummyLocationProvider getCachedAttributes() {
- return mCachedAttributes;
- }
+ if (service == null) return;
- public void run() {
- synchronized (mMutex) {
- if (mServiceConnection != this) {
- // This ServiceConnection no longer the one we want to bind to.
- return;
- }
- ILocationProvider provider = getProvider();
- if (provider == null) {
- return;
+ try {
+ // load properties from provider
+ properties = service.getProperties();
+ if (properties == null) {
+ Log.e(TAG, mServiceWatcher.getBestPackageName() +
+ " has invalid locatino provider properties");
}
- // resend previous values from the location manager if the service has restarted
- try {
- if (mEnabled) {
- provider.enable();
- }
- if (mLocationTracking) {
- provider.enableLocationTracking(true);
+ // apply current state to new service
+ if (enabled) {
+ service.enable();
+ if (request != null) {
+ service.setRequest(request, source);
}
- if (mMinTime >= 0) {
- provider.setMinTime(mMinTime, mMinTimeSource);
- }
- if (mNetworkInfo != null) {
- provider.updateNetworkState(mNetworkState, mNetworkInfo);
- }
- } catch (RemoteException e) {
}
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
- // init cache of parameters
- if (mCachedAttributes == null) {
- try {
- mCachedAttributes = new DummyLocationProvider(mName, null);
- mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
- mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
- mCachedAttributes.setRequiresCell(provider.requiresCell());
- mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
- mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
- mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
- mCachedAttributes.setSupportsBearing(provider.supportsBearing());
- mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
- mCachedAttributes.setAccuracy(provider.getAccuracy());
- } catch (RemoteException e) {
- mCachedAttributes = null;
- }
- }
+ synchronized (mLock) {
+ mProperties = properties;
}
}
};
+ @Override
public String getName() {
return mName;
}
- private DummyLocationProvider getCachedAttributes() {
- synchronized (mMutex) {
- return mServiceConnection.getCachedAttributes();
- }
- }
-
- public boolean requiresNetwork() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresNetwork();
- } else {
- return false;
- }
- }
-
- public boolean requiresSatellite() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresSatellite();
- } else {
- return false;
- }
- }
-
- public boolean requiresCell() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresCell();
- } else {
- return false;
- }
- }
-
- public boolean hasMonetaryCost() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.hasMonetaryCost();
- } else {
- return false;
- }
- }
-
- public boolean supportsAltitude() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsAltitude();
- } else {
- return false;
- }
- }
-
- public boolean supportsSpeed() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsSpeed();
- } else {
- return false;
- }
- }
-
- public boolean supportsBearing() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsBearing();
- } else {
- return false;
- }
- }
-
- public int getPowerRequirement() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.getPowerRequirement();
- } else {
- return -1;
+ @Override
+ public ProviderProperties getProperties() {
+ synchronized (mLock) {
+ return mProperties;
}
}
- public int getAccuracy() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.getAccuracy();
- } else {
- return -1;
- }
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- 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;
- }
-
+ @Override
public void enable() {
- synchronized (mMutex) {
+ synchronized (mLock) {
mEnabled = true;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.enable();
- } catch (RemoteException e) {
- }
- }
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public void disable() {
- synchronized (mMutex) {
- mEnabled = false;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.disable();
- } catch (RemoteException e) {
- }
- }
+ try {
+ service.enable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public boolean isEnabled() {
- synchronized (mMutex) {
- return mEnabled;
+ @Override
+ public void disable() {
+ synchronized (mLock) {
+ mEnabled = false;
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public int getStatus(Bundle extras) {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
+ try {
+ service.disable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
- if (provider != null) {
- try {
- return provider.getStatus(extras);
- } catch (RemoteException e) {
- }
- }
- return 0;
}
- public long getStatusUpdateTime() {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
- if (provider != null) {
- try {
- return provider.getStatusUpdateTime();
- } catch (RemoteException e) {
- }
- }
- return 0;
- }
-
- public String getInternalState() {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
- if (provider != null) {
- try {
- return provider.getInternalState();
- } catch (RemoteException e) {
- Log.e(TAG, "getInternalState failed", e);
- }
+ @Override
+ public boolean isEnabled() {
+ synchronized (mLock) {
+ return mEnabled;
}
- return null;
}
- public boolean isLocationTracking() {
- synchronized (mMutex) {
- return mLocationTracking;
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ synchronized (mLock) {
+ mRequest = request;
+ mWorksource = source;
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public void enableLocationTracking(boolean enable) {
- synchronized (mMutex) {
- mLocationTracking = enable;
- if (!enable) {
- mMinTime = -1;
- mMinTimeSource.clear();
- }
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.enableLocationTracking(enable);
- } catch (RemoteException e) {
- }
- }
+ try {
+ service.setRequest(request, source);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public long getMinTime() {
- synchronized (mMutex) {
- return mMinTime;
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.append("REMOTE SERVICE");
+ pw.append(" name=").append(mName);
+ pw.append(" pkg=").append(mServiceWatcher.getBestPackageName());
+ pw.append(" version=").append("" + mServiceWatcher.getBestVersion());
+ pw.append('\n');
+
+ ILocationProvider service = getService();
+ if (service == null) {
+ pw.println("service down (null)");
+ return;
+ }
+ pw.flush();
+
+ try {
+ service.asBinder().dump(fd, args);
+ } catch (RemoteException e) {
+ pw.println("service down (RemoteException)");
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ pw.println("service down (Exception)");
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public void setMinTime(long minTime, WorkSource ws) {
- synchronized (mMutex) {
- mMinTime = minTime;
- mMinTimeSource.set(ws);
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.setMinTime(minTime, ws);
- } catch (RemoteException e) {
- }
- }
- }
+ @Override
+ public int getStatus(Bundle extras) {
+ ILocationProvider service = getService();
+ if (service == null) return LocationProvider.TEMPORARILY_UNAVAILABLE;
+
+ try {
+ return service.getStatus(extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ return LocationProvider.TEMPORARILY_UNAVAILABLE;
}
- public void updateNetworkState(int state, NetworkInfo info) {
- synchronized (mMutex) {
- mNetworkState = state;
- mNetworkInfo = info;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.updateNetworkState(state, info);
- } catch (RemoteException e) {
- }
- }
- }
- }
+ @Override
+ public long getStatusUpdateTime() {
+ ILocationProvider service = getService();
+ if (service == null) return 0;
- public void updateLocation(Location location) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.updateLocation(location);
- } catch (RemoteException e) {
- }
- }
+ try {
+ return service.getStatusUpdateTime();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
+ return 0;
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- return provider.sendExtraCommand(command, extras);
- } catch (RemoteException e) {
- }
- }
- }
- return false;
- }
+ ILocationProvider service = getService();
+ if (service == null) return false;
- public void addListener(int uid) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.addListener(uid);
- } catch (RemoteException e) {
- }
- }
- }
- }
-
- public void removeListener(int uid) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.removeListener(uid);
- } catch (RemoteException e) {
- }
- }
+ try {
+ return service.sendExtraCommand(command, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
+ return false;
}
-}
+ }
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 09d799f..36c43ff 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -20,15 +20,19 @@ import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
import android.util.PrintWriterPrinter;
+
+import java.io.FileDescriptor;
import java.io.PrintWriter;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
/**
* A mock location provider used by LocationManagerService to implement test providers.
*
@@ -36,60 +40,56 @@ import java.io.PrintWriter;
*/
public class MockProvider implements LocationProviderInterface {
private final String mName;
+ private final ProviderProperties mProperties;
private final ILocationManager mLocationManager;
- private final boolean mRequiresNetwork;
- private final boolean mRequiresSatellite;
- private final boolean mRequiresCell;
- private final boolean mHasMonetaryCost;
- private final boolean mSupportsAltitude;
- private final boolean mSupportsSpeed;
- private final boolean mSupportsBearing;
- private final int mPowerRequirement;
- private final int mAccuracy;
+
private final Location mLocation;
+ private final Bundle mExtras = new Bundle();
+
private int mStatus;
private long mStatusUpdateTime;
- private final Bundle mExtras = new Bundle();
private boolean mHasLocation;
private boolean mHasStatus;
private boolean mEnabled;
private static final String TAG = "MockProvider";
- public MockProvider(String name, ILocationManager locationManager,
- boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ public MockProvider(String name, ILocationManager locationManager,
+ ProviderProperties properties) {
+ if (properties == null) throw new NullPointerException("properties is null");
+
mName = name;
mLocationManager = locationManager;
- mRequiresNetwork = requiresNetwork;
- mRequiresSatellite = requiresSatellite;
- mRequiresCell = requiresCell;
- mHasMonetaryCost = hasMonetaryCost;
- mSupportsAltitude = supportsAltitude;
- mSupportsBearing = supportsBearing;
- mSupportsSpeed = supportsSpeed;
- mPowerRequirement = powerRequirement;
- mAccuracy = accuracy;
+ mProperties = properties;
mLocation = new Location(name);
}
+ @Override
public String getName() {
return mName;
}
+ @Override
+ public ProviderProperties getProperties() {
+ return mProperties;
+ }
+
+ @Override
public void disable() {
mEnabled = false;
}
+ @Override
public void enable() {
mEnabled = true;
}
+ @Override
public boolean isEnabled() {
return mEnabled;
}
+ @Override
public int getStatus(Bundle extras) {
if (mHasStatus) {
extras.clear();
@@ -100,75 +100,20 @@ public class MockProvider implements LocationProviderInterface {
}
}
+ @Override
public long getStatusUpdateTime() {
return mStatusUpdateTime;
}
- public int getAccuracy() {
- return mAccuracy;
- }
-
- public int getPowerRequirement() {
- return mPowerRequirement;
- }
-
- public boolean hasMonetaryCost() {
- return mHasMonetaryCost;
- }
-
- public boolean requiresCell() {
- return mRequiresCell;
- }
-
- public boolean requiresNetwork() {
- return mRequiresNetwork;
- }
-
- public boolean requiresSatellite() {
- return mRequiresSatellite;
- }
-
- public boolean supportsAltitude() {
- return mSupportsAltitude;
- }
-
- public boolean supportsBearing() {
- return mSupportsBearing;
- }
-
- public boolean supportsSpeed() {
- 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;
- try {
- mLocationManager.reportLocation(mLocation, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
+ if (mEnabled) {
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
}
}
@@ -191,34 +136,9 @@ public class MockProvider implements LocationProviderInterface {
mStatusUpdateTime = 0;
}
- public String getInternalState() {
- return null;
- }
-
- public void enableLocationTracking(boolean enable) {
- }
-
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public void setMinTime(long minTime, WorkSource ws) {
- }
-
- public void updateNetworkState(int state, NetworkInfo info) {
- }
-
- public void updateLocation(Location location) {
- }
-
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
-
- public void addListener(int uid) {
- }
-
- public void removeListener(int uid) {
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ dump(pw, "");
}
public void dump(PrintWriter pw, String prefix) {
@@ -231,4 +151,12 @@ public class MockProvider implements LocationProviderInterface {
pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
pw.println(prefix + "mExtras=" + mExtras);
}
+
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) { }
+
+ @Override
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
}
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index ea0d1b0..0ce21b7 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -16,17 +16,23 @@
package com.android.server.location;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
+
/**
* A passive location provider reports locations received from other providers
* for clients that want to listen passively without actually triggering
@@ -35,103 +41,63 @@ import android.util.Log;
* {@hide}
*/
public class PassiveProvider implements LocationProviderInterface {
-
private static final String TAG = "PassiveProvider";
+ private static final ProviderProperties PROPERTIES = new ProviderProperties(
+ false, false, false, false, false, false, false,
+ Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
+
private final ILocationManager mLocationManager;
- private boolean mTracking;
+ private boolean mReportLocation;
public PassiveProvider(ILocationManager locationManager) {
mLocationManager = locationManager;
}
+ @Override
public String getName() {
return LocationManager.PASSIVE_PROVIDER;
}
- public boolean requiresNetwork() {
- return false;
- }
-
- public boolean requiresSatellite() {
- return false;
- }
-
- public boolean requiresCell() {
- return false;
- }
-
- public boolean hasMonetaryCost() {
- return false;
- }
-
- public boolean supportsAltitude() {
- return false;
- }
-
- public boolean supportsSpeed() {
- return false;
- }
-
- public boolean supportsBearing() {
- return false;
- }
-
- public int getPowerRequirement() {
- 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;
+ @Override
+ public ProviderProperties getProperties() {
+ return PROPERTIES;
}
+ @Override
public boolean isEnabled() {
return true;
}
+ @Override
public void enable() {
}
+ @Override
public void disable() {
}
+ @Override
public int getStatus(Bundle extras) {
- if (mTracking) {
+ if (mReportLocation) {
return LocationProvider.AVAILABLE;
} else {
return LocationProvider.TEMPORARILY_UNAVAILABLE;
}
}
+ @Override
public long getStatusUpdateTime() {
return -1;
}
- public String getInternalState() {
- return null;
- }
-
- public void enableLocationTracking(boolean enable) {
- mTracking = enable;
- }
-
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public void setMinTime(long minTime, WorkSource ws) {
- }
-
- public void updateNetworkState(int state, NetworkInfo info) {
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ mReportLocation = request.reportLocation;
}
public void updateLocation(Location location) {
- if (mTracking) {
+ if (mReportLocation) {
try {
// pass the location back to the location manager
mLocationManager.reportLocation(location, true);
@@ -141,13 +107,13 @@ public class PassiveProvider implements LocationProviderInterface {
}
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
return false;
}
- public void addListener(int uid) {
- }
-
- public void removeListener(int uid) {
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("mReportLocaiton=" + mReportLocation);
}
}