1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.location;
18 
19 import android.content.Context;
20 import android.os.IBinder;
21 import android.os.RemoteException;
22 import android.os.ServiceManager;
23 
24 import com.android.internal.util.Preconditions;
25 
26 import java.io.IOException;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.TimeUnit;
32 
33 /**
34  * A class for handling geocoding and reverse geocoding.  Geocoding is
35  * the process of transforming a street address or other description
36  * of a location into a (latitude, longitude) coordinate.  Reverse
37  * geocoding is the process of transforming a (latitude, longitude)
38  * coordinate into a (partial) address.  The amount of detail in a
39  * reverse geocoded location description may vary, for example one
40  * might contain the full street address of the closest building, while
41  * another might contain only a city name and postal code.
42  *
43  * The Geocoder class requires a backend service that is not included in
44  * the core android framework.  The Geocoder query methods will return an
45  * empty list if there no backend service in the platform.  Use the
46  * isPresent() method to determine whether a Geocoder implementation
47  * exists.
48  *
49  * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
50  * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful or
51  * correct. Do not use this API for any safety-critical or regulatory compliance purpose.
52  */
53 public final class Geocoder {
54 
55     private static final long TIMEOUT_MS = 60000;
56 
57     private final GeocoderParams mParams;
58     private final ILocationManager mService;
59 
60     /**
61      * Returns true if the Geocoder methods getFromLocation and
62      * getFromLocationName are implemented.  Lack of network
63      * connectivity may still cause these methods to return null or
64      * empty lists.
65      */
isPresent()66     public static boolean isPresent() {
67         IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
68         ILocationManager lm = ILocationManager.Stub.asInterface(b);
69         try {
70             return lm.geocoderIsPresent();
71         } catch (RemoteException e) {
72             throw e.rethrowFromSystemServer();
73         }
74     }
75 
76     /**
77      * Constructs a Geocoder whose responses will be localized for the
78      * given Locale.
79      *
80      * @param context the Context of the calling Activity
81      * @param locale the desired Locale for the query results
82      *
83      * @throws NullPointerException if Locale is null
84      */
Geocoder(Context context, Locale locale)85     public Geocoder(Context context, Locale locale) {
86         mParams = new GeocoderParams(context, locale);
87         mService = ILocationManager.Stub.asInterface(
88                 ServiceManager.getService(Context.LOCATION_SERVICE));
89     }
90 
91     /**
92      * Constructs a Geocoder whose responses will be localized for the
93      * default system Locale.
94      *
95      * @param context the Context of the calling Activity
96      */
Geocoder(Context context)97     public Geocoder(Context context) {
98         this(context, Locale.getDefault());
99     }
100 
101     /**
102      * Returns an array of Addresses that attempt to describe the area immediately surrounding the
103      * given latitude and longitude. The returned addresses should be localized for the locale
104      * provided to this class's constructor. Results may be obtained by means of a network lookup
105      * and this method may take some time to return, and so should not be called on the main thread.
106      *
107      * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
108      * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
109      * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
110      * purposes.
111      *
112      * @param latitude the latitude a point for the search
113      * @param longitude the longitude a point for the search
114      * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
115      *
116      * @return a list of Address objects. Returns null or empty list if no matches were
117      * found or there is no backend service available.
118      *
119      * @throws IllegalArgumentException if latitude is
120      * less than -90 or greater than 90
121      * @throws IllegalArgumentException if longitude is
122      * less than -180 or greater than 180
123      * @throws IOException if the network is unavailable or any other
124      * I/O problem occurs
125      */
getFromLocation(double latitude, double longitude, int maxResults)126     public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
127             throws IOException {
128         Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
129         Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude");
130 
131         try {
132             GeocodeListener listener = new GeocodeListener();
133             mService.getFromLocation(latitude, longitude, maxResults, mParams, listener);
134             return listener.getResults();
135         } catch (RemoteException e) {
136             throw e.rethrowFromSystemServer();
137         }
138     }
139 
140     /**
141      * Returns an array of Addresses that attempt to describe the named location, which may be a
142      * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain
143      * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be
144      * localized for the locale provided to this class's constructor. Results may be obtained by
145      * means of a network lookup and this method may take some time to return, and so should not be
146      * called on the main thread.
147      *
148      * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
149      * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
150      * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
151      * purposes.
152      *
153      * @param locationName a user-supplied description of a location
154      * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
155      *
156      * @return a list of Address objects. Returns null or empty list if no matches were
157      * found or there is no backend service available.
158      *
159      * @throws IllegalArgumentException if locationName is null
160      * @throws IOException if the network is unavailable or any other
161      * I/O problem occurs
162      */
getFromLocationName(String locationName, int maxResults)163     public List<Address> getFromLocationName(String locationName, int maxResults) throws IOException {
164         return getFromLocationName(locationName, maxResults, 0, 0, 0, 0);
165     }
166 
167     /**
168      * Returns an array of Addresses that attempt to describe the named location, which may be a
169      * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain
170      * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be
171      * localized for the locale provided to this class's constructor. Results may be obtained by
172      * means of a network lookup and this method may take some time to return, and so should not be
173      * called on the main thread.
174      *
175      * <p> You may specify a bounding box for the search results by including the latitude and
176      * longitude of the lower left point and upper right point of the box.
177      *
178      * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
179      * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
180      * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
181      * purposes.
182      *
183      * @param locationName a user-supplied description of a location
184      * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
185      * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
186      * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
187      * @param upperRightLatitude the latitude of the upper right corner of the bounding box
188      * @param upperRightLongitude the longitude of the upper right corner of the bounding box
189      *
190      * @return a list of Address objects. Returns null or empty list if no matches were
191      * found or there is no backend service available.
192      *
193      * @throws IllegalArgumentException if locationName is null
194      * @throws IllegalArgumentException if any latitude is
195      * less than -90 or greater than 90
196      * @throws IllegalArgumentException if any longitude is
197      * less than -180 or greater than 180
198      * @throws IOException if the network is unavailable or any other
199      * I/O problem occurs
200      */
getFromLocationName(String locationName, int maxResults, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude)201     public List<Address> getFromLocationName(String locationName, int maxResults,
202             double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude,
203             double upperRightLongitude) throws IOException {
204         Preconditions.checkArgument(locationName != null);
205         Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude");
206         Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude");
207         Preconditions.checkArgumentInRange(upperRightLatitude, -90.0, 90.0, "upperRightLatitude");
208         Preconditions.checkArgumentInRange(upperRightLongitude, -180.0, 180.0,
209                 "upperRightLongitude");
210 
211         try {
212             GeocodeListener listener = new GeocodeListener();
213             mService.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude,
214                     upperRightLatitude, upperRightLongitude, maxResults, mParams, listener);
215             return listener.getResults();
216         } catch (RemoteException e) {
217             throw e.rethrowFromSystemServer();
218         }
219     }
220 
221     private static class GeocodeListener extends IGeocodeListener.Stub {
222         private final CountDownLatch mLatch = new CountDownLatch(1);
223 
224         private String mError = null;
225         private List<Address> mResults = Collections.emptyList();
226 
GeocodeListener()227         GeocodeListener() {}
228 
229         @Override
onResults(String error, List<Address> results)230         public void onResults(String error, List<Address> results) {
231             mError = error;
232             mResults = results;
233             mLatch.countDown();
234         }
235 
getResults()236         public List<Address> getResults() throws IOException {
237             try {
238                 if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
239                     mError = "Service not Available";
240                 }
241             } catch (InterruptedException e) {
242                 Thread.currentThread().interrupt();
243             }
244 
245             if (mError != null) {
246                 throw new IOException(mError);
247             } else {
248                 return mResults;
249             }
250         }
251     }
252 }
253