Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03e0b893 authored by Soonil Nagarkar's avatar Soonil Nagarkar
Browse files

Add asynchronous geocoding API

Geocoding is unsupported, but the old APIs encourage bad behavior and
force clients to waste a thread blocking for results. This often causes
ANRs in a variety of applications. Provider an asynchronous API so
clients can avoid ANRs.

Bug: 197585308
Test: presubmits
Change-Id: I6fdd90d467f01892ce34371c2161721bd5976ca9
parent 60a7931d
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -19606,14 +19606,22 @@ package android.location {
  }
  public final class Geocoder {
    ctor public Geocoder(android.content.Context, java.util.Locale);
    ctor public Geocoder(android.content.Context);
    method public java.util.List<android.location.Address> getFromLocation(double, double, int) throws java.io.IOException;
    method public java.util.List<android.location.Address> getFromLocationName(String, int) throws java.io.IOException;
    method public java.util.List<android.location.Address> getFromLocationName(String, int, double, double, double, double) throws java.io.IOException;
    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 public static boolean isPresent();
  }
  public static interface Geocoder.GeocodeListener {
    method public default void onError(@Nullable String);
    method public void onGeocode(@NonNull java.util.List<android.location.Address>);
  }
  public final class GnssAntennaInfo implements android.os.Parcelable {
    method public int describeContents();
    method @FloatRange(from=0.0f) public double getCarrierFrequencyMHz();
+225 −84
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

package android.location;

import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;

@@ -27,24 +30,21 @@ import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * A class for handling geocoding and reverse geocoding.  Geocoding is
 * the process of transforming a street address or other description
 * of a location into a (latitude, longitude) coordinate.  Reverse
 * geocoding is the process of transforming a (latitude, longitude)
 * coordinate into a (partial) address.  The amount of detail in a
 * reverse geocoded location description may vary, for 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.
 * A class for handling geocoding and reverse geocoding. Geocoding is the process of transforming a
 * street address or other description of a location into a (latitude, longitude) coordinate.
 * Reverse geocoding is the process of transforming a (latitude, longitude) coordinate into a
 * (partial) address. The amount of detail in a reverse geocoded location description may vary, for
 * 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 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,20 +52,26 @@ import java.util.concurrent.TimeUnit;
 */
public final class Geocoder {

    /** A listener for asynchronous geocoding results. */
    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. */
        default void onError(@Nullable String errorMessage) {}
    }

    private static final long TIMEOUT_MS = 60000;

    private final GeocoderParams mParams;
    private final ILocationManager mService;

    /**
     * Returns true if the Geocoder methods getFromLocation and
     * getFromLocationName are implemented.  Lack of network
     * connectivity may still cause these methods to return null or
     * empty lists.
     * 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.
     */
    public static boolean isPresent() {
        IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
        ILocationManager lm = ILocationManager.Stub.asInterface(b);
        ILocationManager lm = Objects.requireNonNull(ILocationManager.Stub.asInterface(
                ServiceManager.getService(Context.LOCATION_SERVICE)));
        try {
            return lm.geocoderIsPresent();
        } catch (RemoteException e) {
@@ -74,40 +80,35 @@ public final class Geocoder {
    }

    /**
     * Constructs a Geocoder whose responses will be localized for the
     * given Locale.
     *
     * @param context the Context of the calling Activity
     * @param locale the desired Locale for the query results
     *
     * @throws NullPointerException if Locale is null
     * Constructs a Geocoder localized for the default locale.
     */
    public Geocoder(Context context, Locale locale) {
        mParams = new GeocoderParams(context, locale);
        mService = ILocationManager.Stub.asInterface(
                ServiceManager.getService(Context.LOCATION_SERVICE));
    public Geocoder(@NonNull Context context) {
        this(context, Locale.getDefault());
    }

    /**
     * Constructs a Geocoder whose responses will be localized for the
     * default system Locale.
     *
     * @param context the Context of the calling Activity
     * Constructs a Geocoder localized for the given locale.
     */
    public Geocoder(Context context) {
        this(context, Locale.getDefault());
    public Geocoder(@NonNull Context context, @NonNull Locale locale) {
        mParams = new GeocoderParams(context, locale);
        mService = ILocationManager.Stub.asInterface(
                ServiceManager.getService(Context.LOCATION_SERVICE));
    }

    /**
     * Returns an array of Addresses that attempt to describe the area immediately surrounding the
     * given latitude and longitude. The returned addresses should be localized for the locale
     * provided to this class's constructor. Results may be obtained by means of a network lookup
     * and this method may take some time to return, and so should not be called on the main thread.
     * provided to this class's constructor.
     *
     * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
     * <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.
     * purposes.</p>
     *
     * <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>
     *
     * @param latitude the latitude a point for the search
     * @param longitude the longitude a point for the search
@@ -116,22 +117,51 @@ public final class Geocoder {
     * @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 is
     * less than -90 or greater than 90
     * @throws IllegalArgumentException if longitude is
     * less than -180 or greater than 180
     * @throws IOException if the network is unavailable or any other
     * I/O problem occurs
     * @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.
     */
    public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
    @Deprecated
    public @Nullable List<Address> getFromLocation(
            @FloatRange(from = -90D, to = 90D) double latitude,
            @FloatRange(from = -180D, to = 180D)double longitude,
            @IntRange int maxResults)
            throws IOException {
        SynchronousGeocoder listener = new SynchronousGeocoder();
        getFromLocation(latitude, longitude, maxResults, listener);
        return listener.getResults();
    }

    /**
     * Provides an array of Addresses that attempt to describe the area immediately surrounding the
     * given latitude and longitude. The returned addresses should be localized for the locale
     * provided to this class's constructor.
     *
     * <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>
     *
     * @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,
            @NonNull GeocodeListener listener) {
        Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
        Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude");

        try {
            GeocodeListener listener = new GeocodeListener();
            mService.getFromLocation(latitude, longitude, maxResults, mParams, listener);
            return listener.getResults();
            mService.getFromLocation(latitude, longitude, maxResults, mParams,
                    new GeocoderImpl(listener));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -141,14 +171,17 @@ public final class Geocoder {
     * Returns an array of Addresses that attempt to describe the named location, which may be a
     * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain
     * 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. Results may be obtained by
     * means of a network lookup and this method may take some time to return, and so should not be
     * called on the main thread.
     * localized for the locale provided to this class's constructor.
     *
     * <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.
     * purposes.</p>
     *
     * <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>
     *
     * @param locationName a user-supplied description of a location
     * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
@@ -157,20 +190,47 @@ public final class Geocoder {
     * found or there is no backend service available.
     *
     * @throws IllegalArgumentException if locationName is null
     * @throws IOException if the network is unavailable or any other
     * I/O problem occurs
     * @throws IOException if there is a failure
     *
     * @deprecated Use {@link #getFromLocationName(String, int, GeocodeListener)} instead to avoid
     * blocking a thread waiting for results.
     */
    public List<Address> getFromLocationName(String locationName, int maxResults) throws IOException {
    @Deprecated
    public @Nullable List<Address> getFromLocationName(
            @NonNull String locationName,
            @IntRange int maxResults) throws IOException {
        return getFromLocationName(locationName, maxResults, 0, 0, 0, 0);
    }

    /**
     * Provides an array of Addresses that attempt to describe the named location, which may be a
     * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain
     * 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 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>
     *
     * @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,
            @NonNull GeocodeListener listener) {
        getFromLocationName(locationName, maxResults, 0, 0, 0, 0, listener);
    }

    /**
     * Returns an array of Addresses that attempt to describe the named location, which may be a
     * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain
     * 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. Results may be obtained by
     * means of a network lookup and this method may take some time to return, and so should not be
     * called on the main thread.
     * 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
     * longitude of the lower left point and upper right point of the box.
@@ -178,10 +238,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.
     * purposes.</p>
     *
     * <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>
     *
     * @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 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
@@ -191,16 +257,59 @@ public final class Geocoder {
     * found or there is no backend service available.
     *
     * @throws IllegalArgumentException if locationName is null
     * @throws IllegalArgumentException if any latitude is
     * less than -90 or greater than 90
     * @throws IllegalArgumentException if any longitude is
     * less than -180 or greater than 180
     * @throws IOException if the network is unavailable or any other
     * I/O problem occurs
     * @throws IllegalArgumentException if any latitude or longitude is invalid
     * @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.
     */
    public List<Address> getFromLocationName(String locationName, int maxResults,
            double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude,
            double upperRightLongitude) throws IOException {
    @Deprecated
    public @Nullable List<Address> getFromLocationName(
            @NonNull String locationName,
            @IntRange 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 {
        SynchronousGeocoder listener = new SynchronousGeocoder();
        getFromLocationName(locationName, maxResults, lowerLeftLatitude, lowerLeftLongitude,
                upperRightLatitude, upperRightLongitude, listener);
        return listener.getResults();
    }

    /**
     * Returns an array of Addresses that attempt to describe the named location, which may be a
     * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain
     * 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
     * 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>
     *
     * @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
     *
     * @throws IllegalArgumentException if locationName is null
     * @throws IllegalArgumentException if any latitude or longitude is invalid
     */
    public void getFromLocationName(
            @NonNull String locationName,
            @IntRange 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");
@@ -209,27 +318,59 @@ public final class Geocoder {
                "upperRightLongitude");

        try {
            GeocodeListener listener = new GeocodeListener();
            mService.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude,
                    upperRightLatitude, upperRightLongitude, maxResults, mParams, listener);
            return listener.getResults();
                    upperRightLatitude, upperRightLongitude, maxResults, mParams,
                    new GeocoderImpl(listener));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    private static class GeocodeListener extends IGeocodeListener.Stub {
    private static class GeocoderImpl extends IGeocodeListener.Stub {

        private GeocodeListener mListener;

        GeocoderImpl(GeocodeListener listener) {
            mListener = Objects.requireNonNull(listener);
        }

        @Override
        public void onResults(String error, List<Address> addresses) throws RemoteException {
            if (mListener == null) {
                return;
            }

            GeocodeListener listener = mListener;
            mListener = null;

            if (error != null) {
                listener.onError(error);
            } else {
                if (addresses == null) {
                    addresses = Collections.emptyList();
                }
                listener.onGeocode(addresses);
            }
        }
    }

    private static class SynchronousGeocoder implements GeocodeListener {
        private final CountDownLatch mLatch = new CountDownLatch(1);

        private String mError = null;
        private List<Address> mResults = Collections.emptyList();

        GeocodeListener() {}
        SynchronousGeocoder() {}

        @Override
        public void onGeocode(List<Address> addresses) {
            mResults = addresses;
            mLatch.countDown();
        }

        @Override
        public void onResults(String error, List<Address> results) {
            mError = error;
            mResults = results;
        public void onError(String errorMessage) {
            mError = errorMessage;
            mLatch.countDown();
        }