diff options
22 files changed, 1207 insertions, 414 deletions
diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt index 26dc7003a828..adcc3df2d7fe 100644 --- a/boot/hiddenapi/hiddenapi-unsupported.txt +++ b/boot/hiddenapi/hiddenapi-unsupported.txt @@ -133,8 +133,6 @@ Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)La Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService; Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ICountryListener$Stub;-><init>()V -Landroid/location/IGeocodeProvider$Stub;-><init>()V -Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider; Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/location/ILocationListener$Stub;-><init>()V diff --git a/location/api/current.txt b/location/api/current.txt index 5ed8c3c8eb4b..85e9f65a0718 100644 --- a/location/api/current.txt +++ b/location/api/current.txt @@ -88,12 +88,12 @@ package android.location { 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 b1cf96d41497..2e7a541ecb60 100644 --- a/location/api/system-current.txt +++ b/location/api/system-current.txt @@ -591,6 +591,36 @@ package android.location { 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 @@ package android.location.provider { 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 a15834407315..cdde7c66e268 100644 --- a/location/java/android/location/Geocoder.java +++ b/location/java/android/location/Geocoder.java @@ -21,11 +21,13 @@ import android.annotation.IntRange; 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.Locale; 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 @@ import java.util.concurrent.TimeUnit; * 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 @@ import java.util.concurrent.TimeUnit; */ 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 @@ public final class Geocoder { * <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 @@ public final class Geocoder { * <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 @@ public final class Geocoder { * <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 @@ public final class Geocoder { * <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 @@ public final class Geocoder { * 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 @@ public final class Geocoder { * 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 @@ public final class Geocoder { 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 e661ca6cca2e..000000000000 --- 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 72761ef47569..c96c11898671 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -19,13 +19,11 @@ package android.location; 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.LastLocationRequest; 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 @@ interface ILocationManager 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 0fa641b926a9..156be389fe84 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/provider/ForwardGeocodeRequest.aidl b/location/java/android/location/provider/ForwardGeocodeRequest.aidl new file mode 100644 index 000000000000..acd6190aeec8 --- /dev/null +++ b/location/java/android/location/provider/ForwardGeocodeRequest.aidl @@ -0,0 +1,19 @@ +/* + * 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 + * + * 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; + +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 000000000000..8f227b1604b7 --- /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 000000000000..e2c48b9c3515 --- /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 index 8e104119a6f3..cf527130bcc3 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 000000000000..92174387c578 --- /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 index 2484e207dae7..015757ad5d2d 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 000000000000..57c9047f07ca --- /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 c9be5797fdbc..b10019a94209 100644 --- a/location/lib/Android.bp +++ b/location/lib/Android.bp @@ -30,6 +30,7 @@ java_sdk_library { "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 400a7dd3f6ba..ae5187245163 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. - ---- 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. +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. + +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 7046abd8fd3b..75e6bb437ca4 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 @@ package com.android.location.provider { 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/core/java/android/location/GeocoderParams.java b/location/lib/java/android/location/GeocoderParams.java index 3ea6364e07c5..780ccf4ca53c 100644 --- a/core/java/android/location/GeocoderParams.java +++ b/location/lib/java/android/location/GeocoderParams.java @@ -18,7 +18,7 @@ package android.location; import android.annotation.NonNull; import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.SystemApi; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; @@ -28,15 +28,15 @@ import java.util.Locale; import java.util.Objects; /** - * This class contains extra parameters to pass to an IGeocodeProvider - * implementation from the Geocoder class. Currently this contains the - * language, country and variant information from the Geocoder's locale - * as well as the Geocoder client's package name for geocoder server - * logging. This information is kept in a separate class to allow for - * future expansion of the IGeocodeProvider interface. + * 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; @@ -52,7 +52,8 @@ public class GeocoderParams implements Parcelable { this(Process.myUid(), context.getPackageName(), context.getAttributionTag(), locale); } - private GeocoderParams(int uid, String packageName, String attributionTag, Locale locale) { + public GeocoderParams( + int uid, String packageName, @Nullable String attributionTag, Locale locale) { mUid = uid; mPackageName = Objects.requireNonNull(packageName); mAttributionTag = attributionTag; @@ -62,7 +63,6 @@ public class GeocoderParams implements Parcelable { /** * Returns the client UID. */ - @UnsupportedAppUsage public int getClientUid() { return mUid; } @@ -70,7 +70,6 @@ public class GeocoderParams implements Parcelable { /** * Returns the client package name. */ - @UnsupportedAppUsage public @NonNull String getClientPackage() { return mPackageName; } @@ -78,7 +77,6 @@ public class GeocoderParams implements Parcelable { /** * Returns the client attribution tag. */ - @UnsupportedAppUsage public @Nullable String getClientAttributionTag() { return mAttributionTag; } @@ -86,34 +84,38 @@ public class GeocoderParams implements Parcelable { /** * Returns the locale. */ - @UnsupportedAppUsage public @NonNull Locale getLocale() { return mLocale; } public static final @NonNull Parcelable.Creator<GeocoderParams> CREATOR = - new Parcelable.Creator<GeocoderParams>() { - 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]; - } - }; - + 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); diff --git a/location/lib/java/com/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java index 05d793542202..45077ba8adc5 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.ArrayList; 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 diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java deleted file mode 100644 index ac42646499a3..000000000000 --- a/services/core/java/com/android/server/location/GeocoderProxy.java +++ /dev/null @@ -1,122 +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.server.location; - -import android.annotation.Nullable; -import android.content.Context; -import android.location.GeocoderParams; -import android.location.IGeocodeListener; -import android.location.IGeocodeProvider; -import android.os.IBinder; -import android.os.RemoteException; - -import com.android.server.servicewatcher.CurrentUserServiceSupplier; -import com.android.server.servicewatcher.ServiceWatcher; - -import java.util.Collections; - -/** - * Proxy for IGeocodeProvider implementations. - * - * @hide - */ -public class GeocoderProxy { - - private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider"; - - /** - * Creates and registers this proxy. If no suitable service is available for the proxy, returns - * null. - */ - @Nullable - public static GeocoderProxy createAndRegister(Context context) { - GeocoderProxy proxy = new GeocoderProxy(context); - if (proxy.register()) { - return proxy; - } else { - return null; - } - } - - private final ServiceWatcher mServiceWatcher; - - private GeocoderProxy(Context context) { - mServiceWatcher = ServiceWatcher.create(context, "GeocoderProxy", - CurrentUserServiceSupplier.createFromConfig(context, SERVICE_ACTION, - com.android.internal.R.bool.config_enableGeocoderOverlay, - com.android.internal.R.string.config_geocoderProviderPackageName), - null); - } - - private boolean register() { - boolean resolves = mServiceWatcher.checkServiceResolves(); - if (resolves) { - mServiceWatcher.register(); - } - return resolves; - } - - /** - * Geocodes stuff. - */ - public void getFromLocation(double latitude, double longitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - mServiceWatcher.runOnBinder(new ServiceWatcher.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder); - provider.getFromLocation(latitude, longitude, maxResults, params, listener); - } - - @Override - public void onError(Throwable t) { - try { - listener.onResults(t.toString(), Collections.emptyList()); - } catch (RemoteException e) { - // ignore - } - } - }); - } - - /** - * Geocodes stuff. - */ - public void getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - mServiceWatcher.runOnBinder(new ServiceWatcher.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder); - provider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, listener); - } - - @Override - public void onError(Throwable t) { - try { - listener.onResults(t.toString(), Collections.emptyList()); - } catch (RemoteException e) { - // ignore - } - } - }); - } -} diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 323cdc54edae..c5f38553ed81 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -52,13 +52,11 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; 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.IGnssNavigationMessageListener; @@ -75,8 +73,11 @@ import android.location.LocationManagerInternal.LocationPackageTagsListener; import android.location.LocationProvider; 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.location.util.identity.CallerIdentity; import android.os.Binder; import android.os.Build; @@ -141,6 +142,7 @@ import com.android.server.location.provider.MockLocationProvider; import com.android.server.location.provider.PassiveLocationProvider; import com.android.server.location.provider.PassiveLocationProviderManager; import com.android.server.location.provider.StationaryThrottlingLocationProvider; +import com.android.server.location.provider.proxy.ProxyGeocodeProvider; import com.android.server.location.provider.proxy.ProxyLocationProvider; import com.android.server.location.settings.LocationSettings; import com.android.server.location.settings.LocationUserSettings; @@ -253,7 +255,7 @@ public class LocationManagerService extends ILocationManager.Stub implements private final GeofenceManager mGeofenceManager; private volatile @Nullable GnssManagerService mGnssManagerService = null; - private GeocoderProxy mGeocodeProvider; + private ProxyGeocodeProvider mGeocodeProvider; private final Object mDeprecatedGnssBatchingLock = new Object(); @GuardedBy("mDeprecatedGnssBatchingLock") @@ -493,7 +495,7 @@ public class LocationManagerService extends ILocationManager.Stub implements } // bind to geocoder provider - mGeocodeProvider = GeocoderProxy.createAndRegister(mContext); + mGeocodeProvider = ProxyGeocodeProvider.createAndRegister(mContext); if (mGeocodeProvider == null) { Log.e(TAG, "no geocoder provider found"); } @@ -1363,23 +1365,22 @@ public class LocationManagerService extends ILocationManager.Stub implements } @Override - public boolean geocoderIsPresent() { + public boolean isGeocodeAvailable() { return mGeocodeProvider != null; } @Override - public void getFromLocation(double latitude, double longitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - // validate identity - CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(), - params.getClientAttributionTag()); - Preconditions.checkArgument(identity.getUid() == params.getClientUid()); + public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) { + CallerIdentity identity = + CallerIdentity.fromBinder( + mContext, request.getCallingPackage(), request.getCallingAttributionTag()); + Preconditions.checkArgument(identity.getUid() == request.getCallingUid()); if (mGeocodeProvider != null) { - mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, params, listener); + mGeocodeProvider.reverseGeocode(request, callback); } else { try { - listener.onResults(null, Collections.emptyList()); + callback.onError(null); } catch (RemoteException e) { // ignore } @@ -1387,22 +1388,17 @@ public class LocationManagerService extends ILocationManager.Stub implements } @Override - public void getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - // validate identity - CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(), - params.getClientAttributionTag()); - Preconditions.checkArgument(identity.getUid() == params.getClientUid()); + public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) { + CallerIdentity identity = + CallerIdentity.fromBinder( + mContext, request.getCallingPackage(), request.getCallingAttributionTag()); + Preconditions.checkArgument(identity.getUid() == request.getCallingUid()); if (mGeocodeProvider != null) { - mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, listener); + mGeocodeProvider.forwardGeocode(request, callback); } else { try { - listener.onResults(null, Collections.emptyList()); + callback.onError(null); } catch (RemoteException e) { // ignore } diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java new file mode 100644 index 000000000000..ac945f1d2921 --- /dev/null +++ b/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java @@ -0,0 +1,111 @@ +/* + * 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.server.location.provider.proxy; + +import android.annotation.Nullable; +import android.content.Context; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.GeocodeProviderBase; +import android.location.provider.IGeocodeCallback; +import android.location.provider.IGeocodeProvider; +import android.location.provider.ReverseGeocodeRequest; +import android.os.IBinder; +import android.os.RemoteException; + +import com.android.server.servicewatcher.CurrentUserServiceSupplier; +import com.android.server.servicewatcher.ServiceWatcher; + +/** Proxy for IGeocodeProvider implementations. */ +public class ProxyGeocodeProvider { + + /** + * Creates and registers this proxy. If no suitable service is available for the proxy, returns + * null. + */ + @Nullable + public static ProxyGeocodeProvider createAndRegister(Context context) { + ProxyGeocodeProvider proxy = new ProxyGeocodeProvider(context); + if (proxy.register()) { + return proxy; + } else { + return null; + } + } + + private final ServiceWatcher mServiceWatcher; + + private ProxyGeocodeProvider(Context context) { + mServiceWatcher = + ServiceWatcher.create( + context, + "GeocoderProxy", + CurrentUserServiceSupplier.createFromConfig( + context, + GeocodeProviderBase.ACTION_GEOCODE_PROVIDER, + com.android.internal.R.bool.config_enableGeocoderOverlay, + com.android.internal.R.string.config_geocoderProviderPackageName), + null); + } + + private boolean register() { + boolean resolves = mServiceWatcher.checkServiceResolves(); + if (resolves) { + mServiceWatcher.register(); + } + return resolves; + } + + /** Reverse geocodes. */ + public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) { + mServiceWatcher.runOnBinder( + new ServiceWatcher.BinderOperation() { + @Override + public void run(IBinder binder) throws RemoteException { + IGeocodeProvider.Stub.asInterface(binder).reverseGeocode(request, callback); + } + + @Override + public void onError(Throwable t) { + try { + callback.onError(t.toString()); + } catch (RemoteException e) { + // ignore + } + } + }); + } + + /** Forward geocodes. */ + public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) { + mServiceWatcher.runOnBinder( + new ServiceWatcher.BinderOperation() { + @Override + public void run(IBinder binder) throws RemoteException { + IGeocodeProvider.Stub.asInterface(binder).forwardGeocode(request, callback); + } + + @Override + public void onError(Throwable t) { + try { + callback.onError(t.toString()); + } catch (RemoteException e) { + // ignore + } + } + }); + } +} |