Refactor Geocoder Provider System APIs
1) Deprecate old GeocoderProvider API which is not part of any published
API surface.
2) Create new GeocoderProviderBase API which lives in normal SystemApi
surface.
3) Cleanup various callback APIs and geocoding POJOs used internally.
Bug: 279319276
Bug: 229872126
Test: CTS
Change-Id: I1da453ba5f9fd98b08ecdbbbacf640497ed6cd42
diff --git a/location/api/current.txt b/location/api/current.txt
index 5ed8c3c..85e9f65 100644
--- a/location/api/current.txt
+++ b/location/api/current.txt
@@ -88,12 +88,12 @@
public final class Geocoder {
ctor public Geocoder(@NonNull android.content.Context);
ctor public Geocoder(@NonNull android.content.Context, @NonNull java.util.Locale);
- method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange int) throws java.io.IOException;
- method public void getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange int, @NonNull android.location.Geocoder.GeocodeListener);
- method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange int) throws java.io.IOException;
- method public void getFromLocationName(@NonNull String, @IntRange int, @NonNull android.location.Geocoder.GeocodeListener);
- method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double) throws java.io.IOException;
- method public void getFromLocationName(@NonNull String, @IntRange int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @NonNull android.location.Geocoder.GeocodeListener);
+ method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int) throws java.io.IOException;
+ method public void getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int, @NonNull android.location.Geocoder.GeocodeListener);
+ method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange(from=1) int) throws java.io.IOException;
+ method public void getFromLocationName(@NonNull String, @IntRange(from=1) int, @NonNull android.location.Geocoder.GeocodeListener);
+ method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange(from=1) int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double) throws java.io.IOException;
+ method public void getFromLocationName(@NonNull String, @IntRange(from=1) int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @NonNull android.location.Geocoder.GeocodeListener);
method public static boolean isPresent();
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index b1cf96d..2e7a541 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -591,6 +591,36 @@
package android.location.provider {
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getCallingAttributionTag();
+ method @NonNull public String getCallingPackage();
+ method public int getCallingUid();
+ method @NonNull public java.util.Locale getLocale();
+ method @NonNull public String getLocationName();
+ method @FloatRange(from=-90.0, to=90.0) public double getLowerLeftLatitude();
+ method @FloatRange(from=-180.0, to=180.0) public double getLowerLeftLongitude();
+ method @IntRange(from=1) public int getMaxResults();
+ method @FloatRange(from=-90.0, to=90.0) public double getUpperRightLatitude();
+ method @FloatRange(from=-180.0, to=180.0) public double getUpperRightLongitude();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ForwardGeocodeRequest> CREATOR;
+ }
+
+ public static final class ForwardGeocodeRequest.Builder {
+ ctor public ForwardGeocodeRequest.Builder(@NonNull String, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int, @NonNull java.util.Locale, int, @NonNull String);
+ method @NonNull public android.location.provider.ForwardGeocodeRequest build();
+ method @NonNull public android.location.provider.ForwardGeocodeRequest.Builder setCallingAttributionTag(@NonNull String);
+ }
+
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public abstract class GeocodeProviderBase {
+ ctor public GeocodeProviderBase(@NonNull android.content.Context, @NonNull String);
+ method @NonNull public final android.os.IBinder getBinder();
+ method public abstract void onForwardGeocode(@NonNull android.location.provider.ForwardGeocodeRequest, @NonNull android.os.OutcomeReceiver<java.util.List<android.location.Address>,java.lang.Exception>);
+ method public abstract void onReverseGeocode(@NonNull android.location.provider.ReverseGeocodeRequest, @NonNull android.os.OutcomeReceiver<java.util.List<android.location.Address>,java.lang.Exception>);
+ field public static final String ACTION_GEOCODE_PROVIDER = "com.android.location.service.GeocodeProvider";
+ }
+
public abstract class LocationProviderBase {
ctor public LocationProviderBase(@NonNull android.content.Context, @NonNull String, @NonNull android.location.provider.ProviderProperties);
method @Nullable public final android.os.IBinder getBinder();
@@ -642,5 +672,24 @@
method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
}
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ReverseGeocodeRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getCallingAttributionTag();
+ method @NonNull public String getCallingPackage();
+ method public int getCallingUid();
+ method @FloatRange(from=-90.0, to=90.0) public double getLatitude();
+ method @NonNull public java.util.Locale getLocale();
+ method @FloatRange(from=-180.0, to=180.0) public double getLongitude();
+ method @IntRange(from=1) public int getMaxResults();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ReverseGeocodeRequest> CREATOR;
+ }
+
+ public static final class ReverseGeocodeRequest.Builder {
+ ctor public ReverseGeocodeRequest.Builder(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=0) int, @NonNull java.util.Locale, int, @NonNull String);
+ method @NonNull public android.location.provider.ReverseGeocodeRequest build();
+ method @NonNull public android.location.provider.ReverseGeocodeRequest.Builder setCallingAttributionTag(@NonNull String);
+ }
+
}
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index a158344..cdde7c6 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -21,11 +21,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.IGeocodeCallback;
+import android.location.provider.ReverseGeocodeRequest;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import com.android.internal.util.Preconditions;
-
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -33,6 +35,7 @@
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* A class for handling geocoding and reverse geocoding. Geocoding is the process of transforming a
@@ -42,9 +45,12 @@
* example one might contain the full street address of the closest building, while another might
* contain only a city name and postal code.
*
- * The Geocoder class requires a backend service that is not included in the core android framework.
- * The Geocoder query methods will return an empty list if there no backend service in the platform.
- * Use the isPresent() method to determine whether a Geocoder implementation exists.
+ * <p>Use the isPresent() method to determine whether a Geocoder implementation exists on the
+ * current device. If no implementation is present, any attempt to geocode will result in an error.
+ *
+ * <p>Geocoder implementations are only required to make a best effort to return results in the
+ * chosen locale. Note that geocoder implementations may return results in other locales if they
+ * have no information available for the chosen locale.
*
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful or
@@ -52,45 +58,53 @@
*/
public final class Geocoder {
- /** A listener for asynchronous geocoding results. */
+ /**
+ * A listener for asynchronous geocoding results. Only one of the methods will ever be invoked
+ * per geocoding attempt. There are no guarantees on how long it will take for a method to be
+ * invoked, nor any guarantees on the format or availability of error information.
+ */
public interface GeocodeListener {
/** Invoked when geocoding completes successfully. May return an empty list. */
void onGeocode(@NonNull List<Address> addresses);
- /** Invoked when geocoding fails, with a brief error message. */
+
+ /** Invoked when geocoding fails, with an optional error message. */
default void onError(@Nullable String errorMessage) {}
}
- private static final long TIMEOUT_MS = 60000;
+ private static final long TIMEOUT_MS = 15000;
- private final GeocoderParams mParams;
+ private final Context mContext;
+ private final Locale mLocale;
private final ILocationManager mService;
/**
- * Returns true if there is a geocoder implementation present that may return results. If true,
- * there is still no guarantee that any individual geocoding attempt will succeed.
+ * Returns true if there is a geocoder implementation present on the device that may return
+ * results. If true, there is still no guarantee that any individual geocoding attempt will
+ * succeed.
*/
public static boolean isPresent() {
ILocationManager lm = Objects.requireNonNull(ILocationManager.Stub.asInterface(
ServiceManager.getService(Context.LOCATION_SERVICE)));
try {
- return lm.geocoderIsPresent();
+ return lm.isGeocodeAvailable();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- /**
- * Constructs a Geocoder localized for the default locale.
- */
+ /** Constructs a Geocoder localized for {@link Locale#getDefault()}. */
public Geocoder(@NonNull Context context) {
this(context, Locale.getDefault());
}
/**
- * Constructs a Geocoder localized for the given locale.
+ * Constructs a Geocoder localized for the given locale. Note that geocoder implementations will
+ * only make a best effort to return results in the given locale, and there is no guarantee that
+ * returned results will be in the specific locale.
*/
public Geocoder(@NonNull Context context, @NonNull Locale locale) {
- mParams = new GeocoderParams(context, locale);
+ mContext = Objects.requireNonNull(context);
+ mLocale = Objects.requireNonNull(locale);
mService = ILocationManager.Stub.asInterface(
ServiceManager.getService(Context.LOCATION_SERVICE));
}
@@ -103,31 +117,28 @@
* <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for
- * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the
- * asynchronous version of this API. If that is not possible, this should be run on a background
- * thread to avoid blocking other operations.</p>
+ * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this
+ * API. If that is not possible, this should be run on a background thread to avoid blocking
+ * other operations.
*
* @param latitude the latitude a point for the search
* @param longitude the longitude a point for the search
* @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
- *
- * @return a list of Address objects. Returns null or empty list if no matches were
- * found or there is no backend service available.
- *
+ * @return a list of Address objects. Returns null or empty list if no matches were found or
+ * there is no backend service available.
* @throws IllegalArgumentException if latitude or longitude is invalid
* @throws IOException if there is a failure
- *
* @deprecated Use {@link #getFromLocation(double, double, int, GeocodeListener)} instead to
- * avoid blocking a thread waiting for results.
+ * avoid blocking a thread waiting for results.
*/
@Deprecated
public @Nullable List<Address> getFromLocation(
@FloatRange(from = -90D, to = 90D) double latitude,
- @FloatRange(from = -180D, to = 180D)double longitude,
- @IntRange int maxResults)
+ @FloatRange(from = -180D, to = 180D) double longitude,
+ @IntRange(from = 1) int maxResults)
throws IOException {
SynchronousGeocoder listener = new SynchronousGeocoder();
getFromLocation(latitude, longitude, maxResults, listener);
@@ -142,26 +153,32 @@
* <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* @param latitude the latitude a point for the search
* @param longitude the longitude a point for the search
* @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
* @param listener a listener for receiving results
- *
* @throws IllegalArgumentException if latitude or longitude is invalid
*/
public void getFromLocation(
@FloatRange(from = -90D, to = 90D) double latitude,
@FloatRange(from = -180D, to = 180D) double longitude,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@NonNull GeocodeListener listener) {
- Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
- Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude");
-
+ ReverseGeocodeRequest.Builder b =
+ new ReverseGeocodeRequest.Builder(
+ latitude,
+ longitude,
+ maxResults,
+ mLocale,
+ Process.myUid(),
+ mContext.getPackageName());
+ if (mContext.getAttributionTag() != null) {
+ b.setCallingAttributionTag(mContext.getAttributionTag());
+ }
try {
- mService.getFromLocation(latitude, longitude, maxResults, mParams,
- new GeocoderImpl(listener));
+ mService.reverseGeocode(b.build(), new GeocodeCallbackImpl(listener));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -176,29 +193,25 @@
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for
- * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the
- * asynchronous version of this API. If that is not possible, this should be run on a background
- * thread to avoid blocking other operations.</p>
+ * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this
+ * API. If that is not possible, this should be run on a background thread to avoid blocking
+ * other operations.
*
* @param locationName a user-supplied description of a location
* @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
- *
- * @return a list of Address objects. Returns null or empty list if no matches were
- * found or there is no backend service available.
- *
+ * @return a list of Address objects. Returns null or empty list if no matches were found or
+ * there is no backend service available.
* @throws IllegalArgumentException if locationName is null
* @throws IOException if there is a failure
- *
* @deprecated Use {@link #getFromLocationName(String, int, GeocodeListener)} instead to avoid
- * blocking a thread waiting for results.
+ * blocking a thread waiting for results.
*/
@Deprecated
public @Nullable List<Address> getFromLocationName(
- @NonNull String locationName,
- @IntRange int maxResults) throws IOException {
+ @NonNull String locationName, @IntRange(from = 1) int maxResults) throws IOException {
return getFromLocationName(locationName, maxResults, 0, 0, 0, 0);
}
@@ -211,17 +224,16 @@
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* @param locationName a user-supplied description of a location
* @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
* @param listener a listener for receiving results
- *
* @throws IllegalArgumentException if locationName is null
*/
public void getFromLocationName(
@NonNull String locationName,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@NonNull GeocodeListener listener) {
getFromLocationName(locationName, maxResults, 0, 0, 0, 0, listener);
}
@@ -232,45 +244,42 @@
* View, CA", an airport code such as "SFO", and so forth. The returned addresses should be
* localized for the locale provided to this class's constructor.
*
- * <p> You may specify a bounding box for the search results by including the latitude and
+ * <p>You may specify a bounding box for the search results by including the latitude and
* longitude of the lower left point and upper right point of the box.
*
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for
- * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the
- * asynchronous version of this API. If that is not possible, this should be run on a background
- * thread to avoid blocking other operations.</p>
+ * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this
+ * API. If that is not possible, this should be run on a background thread to avoid blocking
+ * other operations.
*
- * @param locationName a user-supplied description of a location
- * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are
- * recommended
- * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
- * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
- * @param upperRightLatitude the latitude of the upper right corner of the bounding box
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
+ * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
+ * @param upperRightLatitude the latitude of the upper right corner of the bounding box
* @param upperRightLongitude the longitude of the upper right corner of the bounding box
- *
- * @return a list of Address objects. Returns null or empty list if no matches were
- * found or there is no backend service available.
- *
+ * @return a list of Address objects. Returns null or empty list if no matches were found or
+ * there is no backend service available.
* @throws IllegalArgumentException if locationName is null
* @throws IllegalArgumentException if any latitude or longitude is invalid
- * @throws IOException if there is a failure
- *
+ * @throws IOException if there is a failure
* @deprecated Use {@link #getFromLocationName(String, int, double, double, double, double,
- * GeocodeListener)} instead to avoid blocking a thread waiting for results.
+ * GeocodeListener)} instead to avoid blocking a thread waiting for results.
*/
@Deprecated
public @Nullable List<Address> getFromLocationName(
@NonNull String locationName,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@FloatRange(from = -90D, to = 90D) double lowerLeftLatitude,
@FloatRange(from = -180D, to = 180D) double lowerLeftLongitude,
@FloatRange(from = -90D, to = 90D) double upperRightLatitude,
- @FloatRange(from = -180D, to = 180D) double upperRightLongitude) throws IOException {
+ @FloatRange(from = -180D, to = 180D) double upperRightLongitude)
+ throws IOException {
SynchronousGeocoder listener = new SynchronousGeocoder();
getFromLocationName(locationName, maxResults, lowerLeftLatitude, lowerLeftLongitude,
upperRightLatitude, upperRightLongitude, listener);
@@ -283,75 +292,79 @@
* View, CA", an airport code such as "SFO", and so forth. The returned addresses should be
* localized for the locale provided to this class's constructor.
*
- * <p> You may specify a bounding box for the search results by including the latitude and
+ * <p>You may specify a bounding box for the search results by including the latitude and
* longitude of the lower left point and upper right point of the box.
*
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
- * @param locationName a user-supplied description of a location
- * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are
- * recommended
- * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
- * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
- * @param upperRightLatitude the latitude of the upper right corner of the bounding box
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
+ * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
+ * @param upperRightLatitude the latitude of the upper right corner of the bounding box
* @param upperRightLongitude the longitude of the upper right corner of the bounding box
- * @param listener a listener for receiving results
- *
+ * @param listener a listener for receiving results
* @throws IllegalArgumentException if locationName is null
* @throws IllegalArgumentException if any latitude or longitude is invalid
*/
public void getFromLocationName(
@NonNull String locationName,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@FloatRange(from = -90D, to = 90D) double lowerLeftLatitude,
@FloatRange(from = -180D, to = 180D) double lowerLeftLongitude,
@FloatRange(from = -90D, to = 90D) double upperRightLatitude,
@FloatRange(from = -180D, to = 180D) double upperRightLongitude,
@NonNull GeocodeListener listener) {
- Preconditions.checkArgument(locationName != null);
- Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude");
- Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude");
- Preconditions.checkArgumentInRange(upperRightLatitude, -90.0, 90.0, "upperRightLatitude");
- Preconditions.checkArgumentInRange(upperRightLongitude, -180.0, 180.0,
- "upperRightLongitude");
-
+ ForwardGeocodeRequest.Builder b =
+ new ForwardGeocodeRequest.Builder(
+ locationName,
+ lowerLeftLatitude,
+ lowerLeftLongitude,
+ upperRightLatitude,
+ upperRightLongitude,
+ maxResults,
+ mLocale,
+ Process.myUid(),
+ mContext.getPackageName());
+ if (mContext.getAttributionTag() != null) {
+ b.setCallingAttributionTag(mContext.getAttributionTag());
+ }
try {
- mService.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude,
- upperRightLatitude, upperRightLongitude, maxResults, mParams,
- new GeocoderImpl(listener));
+ mService.forwardGeocode(b.build(), new GeocodeCallbackImpl(listener));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- private static class GeocoderImpl extends IGeocodeListener.Stub {
+ private static class GeocodeCallbackImpl extends IGeocodeCallback.Stub {
- private GeocodeListener mListener;
+ @Nullable private GeocodeListener mListener;
- GeocoderImpl(GeocodeListener listener) {
+ GeocodeCallbackImpl(GeocodeListener listener) {
mListener = Objects.requireNonNull(listener);
}
@Override
- public void onResults(String error, List<Address> addresses) throws RemoteException {
+ public void onError(@Nullable String error) {
if (mListener == null) {
return;
}
- GeocodeListener listener = mListener;
+ mListener.onError(error);
mListener = null;
+ }
- if (error != null) {
- listener.onError(error);
- } else {
- if (addresses == null) {
- addresses = Collections.emptyList();
- }
- listener.onGeocode(addresses);
+ @Override
+ public void onResults(List<Address> addresses) {
+ if (mListener == null) {
+ return;
}
+
+ mListener.onGeocode(addresses);
+ mListener = null;
}
}
@@ -364,21 +377,21 @@
SynchronousGeocoder() {}
@Override
- public void onGeocode(List<Address> addresses) {
+ public void onGeocode(@NonNull List<Address> addresses) {
mResults = addresses;
mLatch.countDown();
}
@Override
- public void onError(String errorMessage) {
- mError = errorMessage;
+ public void onError(@Nullable String error) {
+ mError = error;
mLatch.countDown();
}
public List<Address> getResults() throws IOException {
try {
if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- mError = "Service not Available";
+ throw new IOException(new TimeoutException());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
diff --git a/location/java/android/location/IGeocodeProvider.aidl b/location/java/android/location/IGeocodeProvider.aidl
deleted file mode 100644
index e661ca6..0000000
--- a/location/java/android/location/IGeocodeProvider.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2009 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.location.Address;
-import android.location.IGeocodeListener;
-import android.location.GeocoderParams;
-
-/**
- * An interface for location providers implementing the Geocoder services.
- *
- * {@hide}
- */
-interface IGeocodeProvider {
-
- oneway void getFromLocation(double latitude, double longitude, int maxResults, in GeocoderParams params, in IGeocodeListener listener);
- oneway void getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude,
- double upperRightLongitude, int maxResults, in GeocoderParams params, in IGeocodeListener listener);
-}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 72761ef..c96c118 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -19,13 +19,11 @@
import android.app.PendingIntent;
import android.location.Address;
import android.location.Criteria;
-import android.location.GeocoderParams;
import android.location.Geofence;
import android.location.GnssAntennaInfo;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementRequest;
-import android.location.IGeocodeListener;
import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssStatusListener;
@@ -37,8 +35,11 @@
import android.location.Location;
import android.location.LocationRequest;
import android.location.LocationTime;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.IGeocodeCallback;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
+import android.location.provider.ReverseGeocodeRequest;
import android.os.Bundle;
import android.os.ICancellationSignal;
import android.os.PackageTagsList;
@@ -68,13 +69,9 @@
void requestGeofence(in Geofence geofence, in PendingIntent intent, String packageName, String attributionTag);
void removeGeofence(in PendingIntent intent);
- boolean geocoderIsPresent();
- void getFromLocation(double latitude, double longitude, int maxResults,
- in GeocoderParams params, in IGeocodeListener listener);
- void getFromLocationName(String locationName,
- double lowerLeftLatitude, double lowerLeftLongitude,
- double upperRightLatitude, double upperRightLongitude, int maxResults,
- in GeocoderParams params, in IGeocodeListener listener);
+ boolean isGeocodeAvailable();
+ void reverseGeocode(in ReverseGeocodeRequest request, in IGeocodeCallback callback);
+ void forwardGeocode(in ForwardGeocodeRequest request, in IGeocodeCallback callback);
GnssCapabilities getGnssCapabilities();
int getGnssYearOfHardware();
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 0fa641b..156be38 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -1,6 +1,13 @@
package: "android.location.flags"
flag {
+ name: "new_geocoder"
+ namespace: "location"
+ description: "Flag for new Geocoder APIs"
+ bug: "229872126"
+}
+
+flag {
name: "location_bypass"
namespace: "location"
description: "Enable location bypass appops behavior"
diff --git a/location/java/android/location/GeocoderParams.aidl b/location/java/android/location/provider/ForwardGeocodeRequest.aidl
similarity index 74%
copy from location/java/android/location/GeocoderParams.aidl
copy to location/java/android/location/provider/ForwardGeocodeRequest.aidl
index 2484e20..acd6190 100644
--- a/location/java/android/location/GeocoderParams.aidl
+++ b/location/java/android/location/provider/ForwardGeocodeRequest.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2010, The Android Open Source Project
+ * Copyright (C) 2024 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.location;
+package android.location.provider;
-parcelable GeocoderParams;
+parcelable ForwardGeocodeRequest;
diff --git a/location/java/android/location/provider/ForwardGeocodeRequest.java b/location/java/android/location/provider/ForwardGeocodeRequest.java
new file mode 100644
index 0000000..8f227b1
--- /dev/null
+++ b/location/java/android/location/provider/ForwardGeocodeRequest.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2023 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.provider;
+
+import static java.lang.Math.max;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Forward geocode (ie from address to lat/lng) provider request.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NEW_GEOCODER)
+@SystemApi
+public final class ForwardGeocodeRequest implements Parcelable {
+
+ private final String mLocationName;
+ private final double mLowerLeftLatitude;
+ private final double mLowerLeftLongitude;
+ private final double mUpperRightLatitude;
+ private final double mUpperRightLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private final String mCallingAttributionTag;
+
+ private ForwardGeocodeRequest(
+ @NonNull String locationName,
+ double lowerLeftLatitude,
+ double lowerLeftLongitude,
+ double upperRightLatitude,
+ double upperRightLongitude,
+ int maxResults,
+ @NonNull Locale locale,
+ int callingUid,
+ @NonNull String callingPackage,
+ @Nullable String callingAttributionTag) {
+ Preconditions.checkArgument(locationName != null, "locationName must not be null");
+ Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude");
+ Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude");
+ Preconditions.checkArgumentInRange(upperRightLatitude, -90.0, 90.0, "upperRightLatitude");
+ Preconditions.checkArgumentInRange(
+ upperRightLongitude, -180.0, 180.0, "upperRightLongitude");
+
+ mLocationName = locationName;
+ mLowerLeftLatitude = lowerLeftLatitude;
+ mLowerLeftLongitude = lowerLeftLongitude;
+ mUpperRightLatitude = upperRightLatitude;
+ mUpperRightLongitude = upperRightLongitude;
+ mMaxResults = max(maxResults, 1);
+ mLocale = Objects.requireNonNull(locale);
+
+ mCallingUid = callingUid;
+ mCallingPackage = Objects.requireNonNull(callingPackage);
+ mCallingAttributionTag = callingAttributionTag;
+ }
+
+ /**
+ * The location name to be forward geocoded. An arbitrary user string that could have any value.
+ */
+ @NonNull
+ public String getLocationName() {
+ return mLocationName;
+ }
+
+ /** The lower left latitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -90.0, to = 90.0)
+ public double getLowerLeftLatitude() {
+ return mLowerLeftLatitude;
+ }
+
+ /** The lower left longitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -180.0, to = 180.0)
+ public double getLowerLeftLongitude() {
+ return mLowerLeftLongitude;
+ }
+
+ /** The upper right latitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -90.0, to = 90.0)
+ public double getUpperRightLatitude() {
+ return mUpperRightLatitude;
+ }
+
+ /** The upper right longitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -180.0, to = 180.0)
+ public double getUpperRightLongitude() {
+ return mUpperRightLongitude;
+ }
+
+ /** The maximum number of forward geocoding results that should be returned. */
+ @IntRange(from = 1)
+ public int getMaxResults() {
+ return mMaxResults;
+ }
+
+ /** The locale that results should be localized to (best effort). */
+ @NonNull
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /** The UID of the caller this geocoding request is happening on behalf of. */
+ public int getCallingUid() {
+ return mCallingUid;
+ }
+
+ /** The package of the caller this geocoding request is happening on behalf of. */
+ @NonNull
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /** The attribution tag of the caller this geocoding request is happening on behalf of. */
+ @Nullable
+ public String getCallingAttributionTag() {
+ return mCallingAttributionTag;
+ }
+
+ public static final @NonNull Creator<ForwardGeocodeRequest> CREATOR =
+ new Creator<>() {
+ @Override
+ public ForwardGeocodeRequest createFromParcel(Parcel in) {
+ return new ForwardGeocodeRequest(
+ Objects.requireNonNull(in.readString8()),
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble(),
+ in.readInt(),
+ new Locale(in.readString8(), in.readString8(), in.readString8()),
+ in.readInt(),
+ Objects.requireNonNull(in.readString8()),
+ in.readString8());
+ }
+
+ @Override
+ public ForwardGeocodeRequest[] newArray(int size) {
+ return new ForwardGeocodeRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeString8(mLocationName);
+ parcel.writeDouble(mLowerLeftLatitude);
+ parcel.writeDouble(mLowerLeftLongitude);
+ parcel.writeDouble(mUpperRightLatitude);
+ parcel.writeDouble(mUpperRightLongitude);
+ parcel.writeInt(mMaxResults);
+ parcel.writeString8(mLocale.getLanguage());
+ parcel.writeString8(mLocale.getCountry());
+ parcel.writeString8(mLocale.getVariant());
+ parcel.writeInt(mCallingUid);
+ parcel.writeString8(mCallingPackage);
+ parcel.writeString8(mCallingAttributionTag);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (object instanceof ForwardGeocodeRequest that) {
+ return mLowerLeftLatitude == that.mLowerLeftLatitude
+ && mLowerLeftLongitude == that.mLowerLeftLongitude
+ && mUpperRightLatitude == that.mUpperRightLatitude
+ && mUpperRightLongitude == that.mUpperRightLongitude
+ && mMaxResults == that.mMaxResults
+ && mCallingUid == that.mCallingUid
+ && mLocale.equals(that.mLocale)
+ && mCallingPackage.equals(that.mCallingPackage)
+ && mLocationName.equals(that.mLocationName)
+ && Objects.equals(mCallingAttributionTag, that.mCallingAttributionTag);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mLocationName,
+ mLowerLeftLatitude,
+ mLowerLeftLongitude,
+ mUpperRightLatitude,
+ mUpperRightLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+
+ /** A Builder for {@link ReverseGeocodeRequest}s. */
+ public static final class Builder {
+
+ private final String mLocationName;
+ private final double mLowerLeftLatitude;
+ private final double mLowerLeftLongitude;
+ private final double mUpperRightLatitude;
+ private final double mUpperRightLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private String mCallingAttributionTag;
+
+ /** Creates a new Builder instance with the given parameters. */
+ public Builder(
+ @NonNull String locationName,
+ @FloatRange(from = -90.0, to = 90.0) double lowerLeftLatitude,
+ @FloatRange(from = -180.0, to = 180.0) double lowerLeftLongitude,
+ @FloatRange(from = -90.0, to = 90.0) double upperRightLatitude,
+ @FloatRange(from = -180.0, to = 180.0) double upperRightLongitude,
+ @IntRange(from = 1) int maxResults,
+ @NonNull Locale locale,
+ int callingUid,
+ @NonNull String callingPackage) {
+ mLocationName = locationName;
+ mLowerLeftLatitude = lowerLeftLatitude;
+ mLowerLeftLongitude = lowerLeftLongitude;
+ mUpperRightLatitude = upperRightLatitude;
+ mUpperRightLongitude = upperRightLongitude;
+ mMaxResults = maxResults;
+ mLocale = locale;
+ mCallingUid = callingUid;
+ mCallingPackage = callingPackage;
+ mCallingAttributionTag = null;
+ }
+
+ /** Sets the attribution tag. */
+ @NonNull
+ public Builder setCallingAttributionTag(@NonNull String attributionTag) {
+ mCallingAttributionTag = attributionTag;
+ return this;
+ }
+
+ /** Builds a {@link ForwardGeocodeRequest}. */
+ @NonNull
+ public ForwardGeocodeRequest build() {
+ return new ForwardGeocodeRequest(
+ mLocationName,
+ mLowerLeftLatitude,
+ mLowerLeftLongitude,
+ mUpperRightLatitude,
+ mUpperRightLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+ }
+}
diff --git a/location/java/android/location/provider/GeocodeProviderBase.java b/location/java/android/location/provider/GeocodeProviderBase.java
new file mode 100644
index 0000000..e2c48b9
--- /dev/null
+++ b/location/java/android/location/provider/GeocodeProviderBase.java
@@ -0,0 +1,174 @@
+/*
+ * 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 android.location.provider;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Address;
+import android.location.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Base class for geocode providers outside the system server.
+ *
+ * <p>Geocode providers should be wrapped in a non-exported service which returns the result of
+ * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
+ * service should not be exported so that components other than the system server cannot bind to it.
+ * Alternatively, the service may be guarded by a permission that only system server can obtain. The
+ * service may specify metadata on its capabilities:
+ *
+ * <ul>
+ * <li>"serviceVersion": An integer version code to help tie break if multiple services are
+ * capable of implementing the geocode provider. All else equal, the service with the highest
+ * version code will be chosen. Assumed to be 0 if not specified.
+ * <li>"serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ * responsibility for handling changes to the current user on the device. If true, the service
+ * will always be bound from the system user. If false, the service will always be bound from
+ * the current user. If the current user changes, the old binding will be released, and a new
+ * binding established under the new user. Assumed to be false if not specified.
+ * </ul>
+ *
+ * <p>The service should have an intent filter in place for the geocode provider as specified by the
+ * constant in this class.
+ *
+ * <p>Geocode providers are identified by their UID / package name / attribution tag. Based on this
+ * identity, geocode providers may be given some special privileges.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NEW_GEOCODER)
+@SystemApi
+public abstract class GeocodeProviderBase {
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the geocode
+ * provider.
+ */
+ @SuppressLint("ActionValue")
+ public static final String ACTION_GEOCODE_PROVIDER =
+ "com.android.location.service.GeocodeProvider";
+
+ final String mTag;
+ @Nullable final String mAttributionTag;
+ final IBinder mBinder;
+
+ /**
+ * Subclasses should pass in a context and an arbitrary tag that may be used for logcat logging
+ * of errors, and thus should uniquely identify the class.
+ */
+ public GeocodeProviderBase(@NonNull Context context, @NonNull String tag) {
+ mTag = tag;
+ mAttributionTag = context.getAttributionTag();
+ mBinder = new Service();
+ }
+
+ /**
+ * Returns the IBinder instance that should be returned from the {@link
+ * android.app.Service#onBind(Intent)} method of the wrapping service.
+ */
+ @NonNull
+ public final IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Requests forward geocoding of the given arguments. The given callback must be invoked once.
+ */
+ public abstract void onForwardGeocode(
+ @NonNull ForwardGeocodeRequest request,
+ @NonNull OutcomeReceiver<List<Address>, Exception> callback);
+
+ /**
+ * Requests reverse geocoding of the given arguments. The given callback must be invoked once.
+ */
+ public abstract void onReverseGeocode(
+ @NonNull ReverseGeocodeRequest request,
+ @NonNull OutcomeReceiver<List<Address>, Exception> callback);
+
+ private class Service extends IGeocodeProvider.Stub {
+ @Override
+ public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) {
+ try {
+ onForwardGeocode(request, new SingleUseCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+
+ @Override
+ public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) {
+ try {
+ onReverseGeocode(request, new SingleUseCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+ }
+
+ private static class SingleUseCallback implements OutcomeReceiver<List<Address>, Exception> {
+
+ private final AtomicReference<IGeocodeCallback> mCallback;
+
+ SingleUseCallback(IGeocodeCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError(e.toString());
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onResult(List<Address> results) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResults(results);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/IGeocodeListener.aidl b/location/java/android/location/provider/IGeocodeCallback.aidl
similarity index 76%
rename from location/java/android/location/IGeocodeListener.aidl
rename to location/java/android/location/provider/IGeocodeCallback.aidl
index 8e10411..cf52713 100644
--- a/location/java/android/location/IGeocodeListener.aidl
+++ b/location/java/android/location/provider/IGeocodeCallback.aidl
@@ -14,16 +14,15 @@
* limitations under the License.
*/
-package android.location;
+package android.location.provider;
import android.location.Address;
/**
- * An interface for returning geocode results.
- *
- * {@hide}
+ * Binder interface for geocoding callbacks.
+ * @hide
*/
-interface IGeocodeListener {
-
- oneway void onResults(String error, in List<Address> results);
+oneway interface IGeocodeCallback {
+ void onError(String error);
+ void onResults(in List<Address> results);
}
diff --git a/location/java/android/location/provider/IGeocodeProvider.aidl b/location/java/android/location/provider/IGeocodeProvider.aidl
new file mode 100644
index 0000000..9217438
--- /dev/null
+++ b/location/java/android/location/provider/IGeocodeProvider.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2009 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.provider;
+
+import android.location.provider.IGeocodeCallback;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.ReverseGeocodeRequest;
+
+/**
+ * Binder interface for services that implement geocode providers. Do not implement this directly,
+ * extend {@link GeocodeProviderBase} instead.
+ * @hide
+ */
+oneway interface IGeocodeProvider {
+ void forwardGeocode(in ForwardGeocodeRequest request, in IGeocodeCallback callback);
+ void reverseGeocode(in ReverseGeocodeRequest request, in IGeocodeCallback callback);
+}
diff --git a/location/java/android/location/GeocoderParams.aidl b/location/java/android/location/provider/ReverseGeocodeRequest.aidl
similarity index 75%
rename from location/java/android/location/GeocoderParams.aidl
rename to location/java/android/location/provider/ReverseGeocodeRequest.aidl
index 2484e20..015757a 100644
--- a/location/java/android/location/GeocoderParams.aidl
+++ b/location/java/android/location/provider/ReverseGeocodeRequest.aidl
@@ -1,11 +1,10 @@
/*
- * Copyright (C) 2010, The Android Open Source Project
- *
+ * Copyright (C) 2024
* 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
+ * 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,
@@ -14,6 +13,6 @@
* limitations under the License.
*/
-package android.location;
+package android.location.provider;
-parcelable GeocoderParams;
+parcelable ReverseGeocodeRequest;
diff --git a/location/java/android/location/provider/ReverseGeocodeRequest.java b/location/java/android/location/provider/ReverseGeocodeRequest.java
new file mode 100644
index 0000000..57c9047
--- /dev/null
+++ b/location/java/android/location/provider/ReverseGeocodeRequest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2023 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.provider;
+
+import static java.lang.Math.max;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Reverse geocode (ie from lat/lng to address) provider request.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NEW_GEOCODER)
+@SystemApi
+public final class ReverseGeocodeRequest implements Parcelable {
+
+ private final double mLatitude;
+ private final double mLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private final String mCallingAttributionTag;
+
+ private ReverseGeocodeRequest(
+ double latitude,
+ double longitude,
+ int maxResults,
+ Locale locale,
+ int callingUid,
+ String callingPackage,
+ @Nullable String callingAttributionTag) {
+ Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
+ Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude");
+
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mMaxResults = max(maxResults, 1);
+ mLocale = Objects.requireNonNull(locale);
+
+ mCallingUid = callingUid;
+ mCallingPackage = Objects.requireNonNull(callingPackage);
+ mCallingAttributionTag = callingAttributionTag;
+ }
+
+ /** The latitude of the point to be reverse geocoded. */
+ @FloatRange(from = -90.0, to = 90.0)
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /** The longitude of the point to be reverse geocoded. */
+ @FloatRange(from = -180.0, to = 180.0)
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /** The maximum number of reverse geocoding results that should be returned. */
+ @IntRange(from = 1)
+ public int getMaxResults() {
+ return mMaxResults;
+ }
+
+ /** The locale that results should be localized to (best effort). */
+ @NonNull
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /** The UID of the caller this geocoding request is happening on behalf of. */
+ public int getCallingUid() {
+ return mCallingUid;
+ }
+
+ /** The package of the caller this geocoding request is happening on behalf of. */
+ @NonNull
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /** The attribution tag of the caller this geocoding request is happening on behalf of. */
+ @Nullable
+ public String getCallingAttributionTag() {
+ return mCallingAttributionTag;
+ }
+
+ public static final @NonNull Creator<ReverseGeocodeRequest> CREATOR =
+ new Creator<>() {
+ @Override
+ public ReverseGeocodeRequest createFromParcel(Parcel in) {
+ return new ReverseGeocodeRequest(
+ in.readDouble(),
+ in.readDouble(),
+ in.readInt(),
+ new Locale(in.readString8(), in.readString8(), in.readString8()),
+ in.readInt(),
+ Objects.requireNonNull(in.readString8()),
+ in.readString8());
+ }
+
+ @Override
+ public ReverseGeocodeRequest[] newArray(int size) {
+ return new ReverseGeocodeRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mLatitude);
+ parcel.writeDouble(mLongitude);
+ parcel.writeInt(mMaxResults);
+ parcel.writeString8(mLocale.getLanguage());
+ parcel.writeString8(mLocale.getCountry());
+ parcel.writeString8(mLocale.getVariant());
+ parcel.writeInt(mCallingUid);
+ parcel.writeString8(mCallingPackage);
+ parcel.writeString8(mCallingAttributionTag);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (object instanceof ReverseGeocodeRequest that) {
+ return mLatitude == that.mLatitude
+ && mLongitude == that.mLongitude
+ && mMaxResults == that.mMaxResults
+ && mCallingUid == that.mCallingUid
+ && mLocale.equals(that.mLocale)
+ && mCallingPackage.equals(that.mCallingPackage)
+ && Objects.equals(mCallingAttributionTag, that.mCallingAttributionTag);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mLatitude,
+ mLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+
+ /** A Builder for {@link ReverseGeocodeRequest}s. */
+ public static final class Builder {
+
+ private final double mLatitude;
+ private final double mLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private String mCallingAttributionTag;
+
+ /** Creates a new Builder instance with the given parameters. */
+ public Builder(
+ @FloatRange(from = -90.0, to = 90.0) double latitude,
+ @FloatRange(from = -180.0, to = 180.0) double longitude,
+ @IntRange(from = 0) int maxResults,
+ @NonNull Locale locale,
+ int callingUid,
+ @NonNull String callingPackage) {
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mMaxResults = maxResults;
+ mLocale = locale;
+ mCallingUid = callingUid;
+ mCallingPackage = callingPackage;
+ mCallingAttributionTag = null;
+ }
+
+ /** Sets the attribution tag. */
+ @NonNull
+ public Builder setCallingAttributionTag(@NonNull String attributionTag) {
+ mCallingAttributionTag = attributionTag;
+ return this;
+ }
+
+ /** Builds a {@link ReverseGeocodeRequest}. */
+ @NonNull
+ public ReverseGeocodeRequest build() {
+ return new ReverseGeocodeRequest(
+ mLatitude,
+ mLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+ }
+}
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index c9be579..b10019a 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -30,6 +30,7 @@
"androidx.annotation_annotation",
],
api_packages: [
+ "android.location",
"com.android.location.provider",
"com.android.location.timezone.provider",
],
diff --git a/location/lib/README.txt b/location/lib/README.txt
index 400a7dd..ae51872 100644
--- a/location/lib/README.txt
+++ b/location/lib/README.txt
@@ -1,30 +1,12 @@
This library (com.android.location.provider.jar) is a shared java library
-containing classes required by unbundled location providers.
+containing classes required by unbundled providers. The library was created
+as a way of exposing API classes outside of the public API before SystemApi
+was possible. Now that SystemApi exists, no new classes should ever be added
+to this library, and all classes in this library should eventually be
+deprecated and new SystemApi replacements offered.
---- Rules of this library ---
-o This library is effectively a PUBLIC API for unbundled location providers
- that may be distributed outside the system image. So it MUST BE API STABLE.
- You can add but not remove. The rules are the same as for the
- public platform SDK API.
-o This library can see and instantiate internal platform classes (such as
- ProviderRequest.java), but it must not expose them in any public method
- (or by extending them via inheritance). This would break clients of the
- library because they cannot see the internal platform classes.
-
-This library is distributed in the system image, and loaded as
-a shared library. So you can change the implementation, but not
-the interface. In this way it is like framework.jar.
-
---- Why does this library exists? ---
-
-Unbundled location providers (such as the NetworkLocationProvider)
-can not use internal platform classes.
-
-So ideally all of these classes would be part of the public platform SDK API,
-but that doesn't seem like a great idea when only applications with a special
-signature can implement this API.
-
-The compromise is this library.
-
-It wraps internal platform classes (like ProviderRequest) with a stable
-API that does not leak the internal classes.
+Whether or not classes in this library can ever be removed must be answered on
+a case by case basis. Most of the classes are usually referenced by Google Play
+services (in which case references can be removed from that code base), but
+these classes may also be referenced by OEM code, which must be considered
+before any removal.
diff --git a/location/lib/api/system-current.txt b/location/lib/api/system-current.txt
index 7046abd..75e6bb4 100644
--- a/location/lib/api/system-current.txt
+++ b/location/lib/api/system-current.txt
@@ -1,4 +1,21 @@
// Signature format: 2.0
+package android.location {
+
+ @Deprecated public class GeocoderParams implements android.os.Parcelable {
+ ctor @Deprecated public GeocoderParams(android.content.Context);
+ ctor @Deprecated public GeocoderParams(android.content.Context, java.util.Locale);
+ ctor @Deprecated public GeocoderParams(int, String, @Nullable String, java.util.Locale);
+ method @Deprecated public int describeContents();
+ method @Deprecated @Nullable public String getClientAttributionTag();
+ method @Deprecated @NonNull public String getClientPackage();
+ method @Deprecated public int getClientUid();
+ method @Deprecated @NonNull public java.util.Locale getLocale();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GeocoderParams> CREATOR;
+ }
+
+}
+
package com.android.location.provider {
@Deprecated public final class FusedLocationHardware {
@@ -25,6 +42,13 @@
method @Deprecated public void onStatusChanged(int);
}
+ @Deprecated public abstract class GeocodeProvider {
+ ctor @Deprecated public GeocodeProvider();
+ method @Deprecated public android.os.IBinder getBinder();
+ method @Deprecated public abstract String onGetFromLocation(double, double, int, android.location.GeocoderParams, java.util.List<android.location.Address>);
+ method @Deprecated public abstract String onGetFromLocationName(String, double, double, double, double, int, android.location.GeocoderParams, java.util.List<android.location.Address>);
+ }
+
@Deprecated public class GmsFusedBatchOptions {
ctor @Deprecated public GmsFusedBatchOptions();
method @Deprecated public int getFlags();
diff --git a/location/lib/java/android/location/GeocoderParams.java b/location/lib/java/android/location/GeocoderParams.java
new file mode 100644
index 0000000..780ccf4
--- /dev/null
+++ b/location/lib/java/android/location/GeocoderParams.java
@@ -0,0 +1,127 @@
+/*
+ * 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 android.location;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Process;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * This class was originally shipped out-of-band from the normal API processes as a separate drop
+ * before SystemApi existed. Now that SystemApi does exist, this class has been retroactively
+ * published through SystemApi.
+ *
+ * @deprecated Do not use.
+ * @hide
+ */
+@Deprecated
+@SystemApi
+public class GeocoderParams implements Parcelable {
+
+ private final int mUid;
+ private final String mPackageName;
+ private final @Nullable String mAttributionTag;
+ private final Locale mLocale;
+
+ public GeocoderParams(Context context) {
+ this(context, Locale.getDefault());
+ }
+
+ public GeocoderParams(Context context, Locale locale) {
+ this(Process.myUid(), context.getPackageName(), context.getAttributionTag(), locale);
+ }
+
+ public GeocoderParams(
+ int uid, String packageName, @Nullable String attributionTag, Locale locale) {
+ mUid = uid;
+ mPackageName = Objects.requireNonNull(packageName);
+ mAttributionTag = attributionTag;
+ mLocale = Objects.requireNonNull(locale);
+ }
+
+ /**
+ * Returns the client UID.
+ */
+ public int getClientUid() {
+ return mUid;
+ }
+
+ /**
+ * Returns the client package name.
+ */
+ public @NonNull String getClientPackage() {
+ return mPackageName;
+ }
+
+ /**
+ * Returns the client attribution tag.
+ */
+ public @Nullable String getClientAttributionTag() {
+ return mAttributionTag;
+ }
+
+ /**
+ * Returns the locale.
+ */
+ public @NonNull Locale getLocale() {
+ return mLocale;
+ }
+
+ public static final @NonNull Parcelable.Creator<GeocoderParams> CREATOR =
+ new Parcelable.Creator<>() {
+ public GeocoderParams createFromParcel(Parcel in) {
+ int uid = in.readInt();
+ String packageName = in.readString8();
+ String attributionTag = in.readString8();
+ String language = in.readString8();
+ String country = in.readString8();
+ String variant = in.readString8();
+
+ return new GeocoderParams(
+ uid,
+ packageName,
+ attributionTag,
+ new Locale(language, country, variant));
+ }
+
+ public GeocoderParams[] newArray(int size) {
+ return new GeocoderParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mUid);
+ parcel.writeString8(mPackageName);
+ parcel.writeString8(mAttributionTag);
+ parcel.writeString8(mLocale.getLanguage());
+ parcel.writeString8(mLocale.getCountry());
+ parcel.writeString8(mLocale.getVariant());
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java
index 05d7935..45077ba 100644
--- a/location/lib/java/com/android/location/provider/GeocodeProvider.java
+++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java
@@ -16,10 +16,13 @@
package com.android.location.provider;
+import android.annotation.SystemApi;
import android.location.Address;
import android.location.GeocoderParams;
-import android.location.IGeocodeListener;
-import android.location.IGeocodeProvider;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.IGeocodeCallback;
+import android.location.provider.IGeocodeProvider;
+import android.location.provider.ReverseGeocodeRequest;
import android.os.IBinder;
import android.os.RemoteException;
@@ -27,47 +30,74 @@
import java.util.List;
/**
- * Base class for geocode providers implemented as unbundled services.
+ * This class was originally shipped out-of-band from the normal API processes as a separate drop
+ * before SystemApi existed. Now that SystemApi does exist, this class has been retroactively
+ * published through SystemApi.
*
- * <p>Geocode providers can be implemented as services and return the result of
- * {@link GeocodeProvider#getBinder()} in its getBinder() method.
- *
- * <p>IMPORTANT: This class is effectively a public API for unbundled
- * applications, and must remain API stable. See README.txt in the root
- * of this package for more information.
+ * @deprecated Use {@link android.location.provider.GeocodeProviderBase} instead.
* @hide
*/
+@Deprecated
+@SystemApi
public abstract class GeocodeProvider {
- private IGeocodeProvider.Stub mProvider = new IGeocodeProvider.Stub() {
- @Override
- public void getFromLocation(double latitude, double longitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- List<Address> results = new ArrayList<>();
- String error = onGetFromLocation(latitude, longitude, maxResults, params, results);
- try {
- listener.onResults(error, results);
- } catch (RemoteException e) {
- // ignore
- }
- }
+ private final IGeocodeProvider.Stub mProvider =
+ new IGeocodeProvider.Stub() {
+ @Override
+ public void reverseGeocode(
+ ReverseGeocodeRequest request, IGeocodeCallback callback) {
+ List<Address> results = new ArrayList<>();
+ String error =
+ onGetFromLocation(
+ request.getLatitude(),
+ request.getLongitude(),
+ request.getMaxResults(),
+ new GeocoderParams(
+ request.getCallingUid(),
+ request.getCallingPackage(),
+ request.getCallingAttributionTag(),
+ request.getLocale()),
+ results);
+ try {
+ if (error != null) {
+ callback.onError(error);
+ } else {
+ callback.onResults(results);
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
- @Override
- public void getFromLocationName(String locationName,
- double lowerLeftLatitude, double lowerLeftLongitude,
- double upperRightLatitude, double upperRightLongitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- List<Address> results = new ArrayList<>();
- String error = onGetFromLocationName(locationName, lowerLeftLatitude,
- lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
- maxResults, params, results);
- try {
- listener.onResults(error, results);
- } catch (RemoteException e) {
- // ignore
- }
- }
- };
+ @Override
+ public void forwardGeocode(
+ ForwardGeocodeRequest request, IGeocodeCallback callback) {
+ List<Address> results = new ArrayList<>();
+ String error =
+ onGetFromLocationName(
+ request.getLocationName(),
+ request.getLowerLeftLatitude(),
+ request.getLowerLeftLongitude(),
+ request.getUpperRightLatitude(),
+ request.getUpperRightLongitude(),
+ request.getMaxResults(),
+ new GeocoderParams(
+ request.getCallingUid(),
+ request.getCallingPackage(),
+ request.getCallingAttributionTag(),
+ request.getLocale()),
+ results);
+ try {
+ if (error != null) {
+ callback.onError(error);
+ } else {
+ callback.onResults(results);
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ };
/**
* This method is overridden to implement the