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
17package android.location;
18
19import com.android.internal.location.ProviderProperties;
20
21import android.annotation.SystemApi;
22import android.app.PendingIntent;
23import android.content.Context;
24import android.content.Intent;
25import android.os.Build;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Looper;
29import android.os.Message;
30import android.os.RemoteException;
31import android.util.Log;
32
33import java.util.ArrayList;
34import java.util.HashMap;
35import java.util.List;
36
37/**
38 * This class provides access to the system location services.  These
39 * services allow applications to obtain periodic updates of the
40 * device's geographical location, or to fire an application-specified
41 * {@link Intent} when the device enters the proximity of a given
42 * geographical location.
43 *
44 * <p>You do not
45 * instantiate this class directly; instead, retrieve it through
46 * {@link android.content.Context#getSystemService
47 * Context.getSystemService(Context.LOCATION_SERVICE)}.
48 *
49 * <p class="note">Unless noted, all Location API methods require
50 * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
51 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
52 * If your application only has the coarse permission then it will not have
53 * access to the GPS or passive location providers. Other providers will still
54 * return location results, but the update rate will be throttled and the exact
55 * location will be obfuscated to a coarse level of accuracy.
56 */
57public class LocationManager {
58    private static final String TAG = "LocationManager";
59
60    private final Context mContext;
61    private final ILocationManager mService;
62    private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport;
63    private final GpsNavigationMessageListenerTransport mGpsNavigationMessageListenerTransport;
64    private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
65            new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
66    private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
67            new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
68    private final GpsStatus mGpsStatus = new GpsStatus();
69
70    /**
71     * Name of the network location provider.
72     * <p>This provider determines location based on
73     * availability of cell tower and WiFi access points. Results are retrieved
74     * by means of a network lookup.
75     */
76    public static final String NETWORK_PROVIDER = "network";
77
78    /**
79     * Name of the GPS location provider.
80     *
81     * <p>This provider determines location using
82     * satellites. Depending on conditions, this provider may take a while to return
83     * a location fix. Requires the permission
84     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
85     *
86     * <p> The extras Bundle for the GPS location provider can contain the
87     * following key/value pairs:
88     * <ul>
89     * <li> satellites - the number of satellites used to derive the fix
90     * </ul>
91     */
92    public static final String GPS_PROVIDER = "gps";
93
94    /**
95     * A special location provider for receiving locations without actually initiating
96     * a location fix.
97     *
98     * <p>This provider can be used to passively receive location updates
99     * when other applications or services request them without actually requesting
100     * the locations yourself.  This provider will return locations generated by other
101     * providers.  You can query the {@link Location#getProvider()} method to determine
102     * the origin of the location update. Requires the permission
103     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, although if the GPS is
104     * not enabled this provider might only return coarse fixes.
105     */
106    public static final String PASSIVE_PROVIDER = "passive";
107
108    /**
109     * Name of the Fused location provider.
110     *
111     * <p>This provider combines inputs for all possible location sources
112     * to provide the best possible Location fix. It is implicitly
113     * used for all API's that involve the {@link LocationRequest}
114     * object.
115     *
116     * @hide
117     */
118    public static final String FUSED_PROVIDER = "fused";
119
120    /**
121     * Key used for the Bundle extra holding a boolean indicating whether
122     * a proximity alert is entering (true) or exiting (false)..
123     */
124    public static final String KEY_PROXIMITY_ENTERING = "entering";
125
126    /**
127     * Key used for a Bundle extra holding an Integer status value
128     * when a status change is broadcast using a PendingIntent.
129     */
130    public static final String KEY_STATUS_CHANGED = "status";
131
132    /**
133     * Key used for a Bundle extra holding an Boolean status value
134     * when a provider enabled/disabled event is broadcast using a PendingIntent.
135     */
136    public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
137
138    /**
139     * Key used for a Bundle extra holding a Location value
140     * when a location change is broadcast using a PendingIntent.
141     */
142    public static final String KEY_LOCATION_CHANGED = "location";
143
144    /**
145     * Broadcast intent action indicating that the GPS has either been
146     * enabled or disabled. An intent extra provides this state as a boolean,
147     * where {@code true} means enabled.
148     * @see #EXTRA_GPS_ENABLED
149     *
150     * @hide
151     */
152    public static final String GPS_ENABLED_CHANGE_ACTION =
153        "android.location.GPS_ENABLED_CHANGE";
154
155    /**
156     * Broadcast intent action when the configured location providers
157     * change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the
158     * {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION}
159     * instead.
160     */
161    public static final String PROVIDERS_CHANGED_ACTION =
162        "android.location.PROVIDERS_CHANGED";
163
164    /**
165     * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes.
166     * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
167     * If you're interacting with {@link #isProviderEnabled(String)}, use
168     * {@link #PROVIDERS_CHANGED_ACTION} instead.
169     *
170     * In the future, there may be mode changes that do not result in
171     * {@link #PROVIDERS_CHANGED_ACTION} broadcasts.
172     */
173    public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
174
175    /**
176     * Broadcast intent action indicating that the GPS has either started or
177     * stopped receiving GPS fixes. An intent extra provides this state as a
178     * boolean, where {@code true} means that the GPS is actively receiving fixes.
179     * @see #EXTRA_GPS_ENABLED
180     *
181     * @hide
182     */
183    public static final String GPS_FIX_CHANGE_ACTION =
184        "android.location.GPS_FIX_CHANGE";
185
186    /**
187     * The lookup key for a boolean that indicates whether GPS is enabled or
188     * disabled. {@code true} means GPS is enabled. Retrieve it with
189     * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
190     *
191     * @hide
192     */
193    public static final String EXTRA_GPS_ENABLED = "enabled";
194
195    /**
196     * Broadcast intent action indicating that a high power location requests
197     * has either started or stopped being active.  The current state of
198     * active location requests should be read from AppOpsManager using
199     * {@code OP_MONITOR_HIGH_POWER_LOCATION}.
200     *
201     * @hide
202     */
203    public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
204        "android.location.HIGH_POWER_REQUEST_CHANGE";
205
206    // Map from LocationListeners to their associated ListenerTransport objects
207    private HashMap<LocationListener,ListenerTransport> mListeners =
208        new HashMap<LocationListener,ListenerTransport>();
209
210    private class ListenerTransport extends ILocationListener.Stub {
211        private static final int TYPE_LOCATION_CHANGED = 1;
212        private static final int TYPE_STATUS_CHANGED = 2;
213        private static final int TYPE_PROVIDER_ENABLED = 3;
214        private static final int TYPE_PROVIDER_DISABLED = 4;
215
216        private LocationListener mListener;
217        private final Handler mListenerHandler;
218
219        ListenerTransport(LocationListener listener, Looper looper) {
220            mListener = listener;
221
222            if (looper == null) {
223                mListenerHandler = new Handler() {
224                    @Override
225                    public void handleMessage(Message msg) {
226                        _handleMessage(msg);
227                    }
228                };
229            } else {
230                mListenerHandler = new Handler(looper) {
231                    @Override
232                    public void handleMessage(Message msg) {
233                        _handleMessage(msg);
234                    }
235                };
236            }
237        }
238
239        @Override
240        public void onLocationChanged(Location location) {
241            Message msg = Message.obtain();
242            msg.what = TYPE_LOCATION_CHANGED;
243            msg.obj = location;
244            mListenerHandler.sendMessage(msg);
245        }
246
247        @Override
248        public void onStatusChanged(String provider, int status, Bundle extras) {
249            Message msg = Message.obtain();
250            msg.what = TYPE_STATUS_CHANGED;
251            Bundle b = new Bundle();
252            b.putString("provider", provider);
253            b.putInt("status", status);
254            if (extras != null) {
255                b.putBundle("extras", extras);
256            }
257            msg.obj = b;
258            mListenerHandler.sendMessage(msg);
259        }
260
261        @Override
262        public void onProviderEnabled(String provider) {
263            Message msg = Message.obtain();
264            msg.what = TYPE_PROVIDER_ENABLED;
265            msg.obj = provider;
266            mListenerHandler.sendMessage(msg);
267        }
268
269        @Override
270        public void onProviderDisabled(String provider) {
271            Message msg = Message.obtain();
272            msg.what = TYPE_PROVIDER_DISABLED;
273            msg.obj = provider;
274            mListenerHandler.sendMessage(msg);
275        }
276
277        private void _handleMessage(Message msg) {
278            switch (msg.what) {
279                case TYPE_LOCATION_CHANGED:
280                    Location location = new Location((Location) msg.obj);
281                    mListener.onLocationChanged(location);
282                    break;
283                case TYPE_STATUS_CHANGED:
284                    Bundle b = (Bundle) msg.obj;
285                    String provider = b.getString("provider");
286                    int status = b.getInt("status");
287                    Bundle extras = b.getBundle("extras");
288                    mListener.onStatusChanged(provider, status, extras);
289                    break;
290                case TYPE_PROVIDER_ENABLED:
291                    mListener.onProviderEnabled((String) msg.obj);
292                    break;
293                case TYPE_PROVIDER_DISABLED:
294                    mListener.onProviderDisabled((String) msg.obj);
295                    break;
296            }
297            try {
298                mService.locationCallbackFinished(this);
299            } catch (RemoteException e) {
300                Log.e(TAG, "locationCallbackFinished: RemoteException", e);
301            }
302        }
303    }
304
305    /**
306     * @hide - hide this constructor because it has a parameter
307     * of type ILocationManager, which is a system private class. The
308     * right way to create an instance of this class is using the
309     * factory Context.getSystemService.
310     */
311    public LocationManager(Context context, ILocationManager service) {
312        mService = service;
313        mContext = context;
314        mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService);
315        mGpsNavigationMessageListenerTransport =
316                new GpsNavigationMessageListenerTransport(mContext, mService);
317    }
318
319    private LocationProvider createProvider(String name, ProviderProperties properties) {
320        return new LocationProvider(name, properties);
321    }
322
323    /**
324     * Returns a list of the names of all known location providers.
325     * <p>All providers are returned, including ones that are not permitted to
326     * be accessed by the calling activity or are currently disabled.
327     *
328     * @return list of Strings containing names of the provider
329     */
330    public List<String> getAllProviders() {
331        try {
332            return mService.getAllProviders();
333        } catch (RemoteException e) {
334            Log.e(TAG, "RemoteException", e);
335        }
336        return null;
337    }
338
339    /**
340     * Returns a list of the names of location providers.
341     *
342     * @param enabledOnly if true then only the providers which are currently
343     * enabled are returned.
344     * @return list of Strings containing names of the providers
345     */
346    public List<String> getProviders(boolean enabledOnly) {
347        try {
348            return mService.getProviders(null, enabledOnly);
349        } catch (RemoteException e) {
350            Log.e(TAG, "RemoteException", e);
351        }
352        return null;
353    }
354
355    /**
356     * Returns the information associated with the location provider of the
357     * given name, or null if no provider exists by that name.
358     *
359     * @param name the provider name
360     * @return a LocationProvider, or null
361     *
362     * @throws IllegalArgumentException if name is null or does not exist
363     * @throws SecurityException if the caller is not permitted to access the
364     * given provider.
365     */
366    public LocationProvider getProvider(String name) {
367        checkProvider(name);
368        try {
369            ProviderProperties properties = mService.getProviderProperties(name);
370            if (properties == null) {
371                return null;
372            }
373            return createProvider(name, properties);
374        } catch (RemoteException e) {
375            Log.e(TAG, "RemoteException", e);
376        }
377        return null;
378    }
379
380    /**
381     * Returns a list of the names of LocationProviders that satisfy the given
382     * criteria, or null if none do.  Only providers that are permitted to be
383     * accessed by the calling activity will be returned.
384     *
385     * @param criteria the criteria that the returned providers must match
386     * @param enabledOnly if true then only the providers which are currently
387     * enabled are returned.
388     * @return list of Strings containing names of the providers
389     */
390    public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
391        checkCriteria(criteria);
392        try {
393            return mService.getProviders(criteria, enabledOnly);
394        } catch (RemoteException e) {
395            Log.e(TAG, "RemoteException", e);
396        }
397        return null;
398    }
399
400    /**
401     * Returns the name of the provider that best meets the given criteria. Only providers
402     * that are permitted to be accessed by the calling activity will be
403     * returned.  If several providers meet the criteria, the one with the best
404     * accuracy is returned.  If no provider meets the criteria,
405     * the criteria are loosened in the following sequence:
406     *
407     * <ul>
408     * <li> power requirement
409     * <li> accuracy
410     * <li> bearing
411     * <li> speed
412     * <li> altitude
413     * </ul>
414     *
415     * <p> Note that the requirement on monetary cost is not removed
416     * in this process.
417     *
418     * @param criteria the criteria that need to be matched
419     * @param enabledOnly if true then only a provider that is currently enabled is returned
420     * @return name of the provider that best matches the requirements
421     */
422    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
423        checkCriteria(criteria);
424        try {
425            return mService.getBestProvider(criteria, enabledOnly);
426        } catch (RemoteException e) {
427            Log.e(TAG, "RemoteException", e);
428        }
429        return null;
430    }
431
432    /**
433     * Register for location updates using the named provider, and a
434     * pending intent.
435     *
436     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
437     * for more detail on how to use this method.
438     *
439     * @param provider the name of the provider with which to register
440     * @param minTime minimum time interval between location updates, in milliseconds
441     * @param minDistance minimum distance between location updates, in meters
442     * @param listener a {@link LocationListener} whose
443     * {@link LocationListener#onLocationChanged} method will be called for
444     * each location update
445     *
446     * @throws IllegalArgumentException if provider is null or doesn't exist
447     * on this device
448     * @throws IllegalArgumentException if listener is null
449     * @throws RuntimeException if the calling thread has no Looper
450     * @throws SecurityException if no suitable permission is present
451     */
452    public void requestLocationUpdates(String provider, long minTime, float minDistance,
453            LocationListener listener) {
454        checkProvider(provider);
455        checkListener(listener);
456
457        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
458                provider, minTime, minDistance, false);
459        requestLocationUpdates(request, listener, null, null);
460    }
461
462    /**
463     * Register for location updates using the named provider, and a callback on
464     * the specified looper thread.
465     *
466     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
467     * for more detail on how to use this method.
468     *
469     * @param provider the name of the provider with which to register
470     * @param minTime minimum time interval between location updates, in milliseconds
471     * @param minDistance minimum distance between location updates, in meters
472     * @param listener a {@link LocationListener} whose
473     * {@link LocationListener#onLocationChanged} method will be called for
474     * each location update
475     * @param looper a Looper object whose message queue will be used to
476     * implement the callback mechanism, or null to make callbacks on the calling
477     * thread
478     *
479     * @throws IllegalArgumentException if provider is null or doesn't exist
480     * @throws IllegalArgumentException if listener is null
481     * @throws SecurityException if no suitable permission is present
482     */
483    public void requestLocationUpdates(String provider, long minTime, float minDistance,
484            LocationListener listener, Looper looper) {
485        checkProvider(provider);
486        checkListener(listener);
487
488        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
489                provider, minTime, minDistance, false);
490        requestLocationUpdates(request, listener, looper, null);
491    }
492
493    /**
494     * Register for location updates using a Criteria, and a callback
495     * on the specified looper thread.
496     *
497     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
498     * for more detail on how to use this method.
499     *
500     * @param minTime minimum time interval between location updates, in milliseconds
501     * @param minDistance minimum distance between location updates, in meters
502     * @param criteria contains parameters for the location manager to choose the
503     * appropriate provider and parameters to compute the location
504     * @param listener a {@link LocationListener} whose
505     * {@link LocationListener#onLocationChanged} method will be called for
506     * each location update
507     * @param looper a Looper object whose message queue will be used to
508     * implement the callback mechanism, or null to make callbacks on the calling
509     * thread
510     *
511     * @throws IllegalArgumentException if criteria is null
512     * @throws IllegalArgumentException if listener is null
513     * @throws SecurityException if no suitable permission is present
514     */
515    public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
516            LocationListener listener, Looper looper) {
517        checkCriteria(criteria);
518        checkListener(listener);
519
520        LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
521                criteria, minTime, minDistance, false);
522        requestLocationUpdates(request, listener, looper, null);
523    }
524
525    /**
526     * Register for location updates using the named provider, and a
527     * pending intent.
528     *
529     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
530     * for more detail on how to use this method.
531     *
532     * @param provider the name of the provider with which to register
533     * @param minTime minimum time interval between location updates, in milliseconds
534     * @param minDistance minimum distance between location updates, in meters
535     * @param intent a {@link PendingIntent} to be sent for each location update
536     *
537     * @throws IllegalArgumentException if provider is null or doesn't exist
538     * on this device
539     * @throws IllegalArgumentException if intent is null
540     * @throws SecurityException if no suitable permission is present
541     */
542    public void requestLocationUpdates(String provider, long minTime, float minDistance,
543            PendingIntent intent) {
544        checkProvider(provider);
545        checkPendingIntent(intent);
546
547        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
548                provider, minTime, minDistance, false);
549        requestLocationUpdates(request, null, null, intent);
550    }
551
552    /**
553     * Register for location updates using a Criteria and pending intent.
554     *
555     * <p>The <code>requestLocationUpdates()</code> and
556     * <code>requestSingleUpdate()</code> register the current activity to be
557     * updated periodically by the named provider, or by the provider matching
558     * the specified {@link Criteria}, with location and status updates.
559     *
560     * <p> It may take a while to receive the first location update. If
561     * an immediate location is required, applications may use the
562     * {@link #getLastKnownLocation(String)} method.
563     *
564     * <p> Location updates are received either by {@link LocationListener}
565     * callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
566     *
567     * <p> If the caller supplied a pending intent, then location updates
568     * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
569     * {@link android.location.Location} value.
570     *
571     * <p> The location update interval can be controlled using the minTime parameter.
572     * The elapsed time between location updates will never be less than
573     * minTime, although it can be more depending on the Location Provider
574     * implementation and the update interval requested by other applications.
575     *
576     * <p> Choosing a sensible value for minTime is important to conserve
577     * battery life. Each location update requires power from
578     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
579     * possible while still providing a reasonable user experience.
580     * If your application is not in the foreground and showing
581     * location to the user then your application should avoid using an active
582     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
583     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
584     * or greater. If your application is in the foreground and showing
585     * location to the user then it is appropriate to select a faster
586     * update interval.
587     *
588     * <p> The minDistance parameter can also be used to control the
589     * frequency of location updates. If it is greater than 0 then the
590     * location provider will only send your application an update when
591     * the location has changed by at least minDistance meters, AND
592     * at least minTime milliseconds have passed. However it is more
593     * difficult for location providers to save power using the minDistance
594     * parameter, so minTime should be the primary tool to conserving battery
595     * life.
596     *
597     * <p> If your application wants to passively observe location
598     * updates triggered by other applications, but not consume
599     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
600     * This provider does not actively turn on or modify active location
601     * providers, so you do not need to be as careful about minTime and
602     * minDistance. However if your application performs heavy work
603     * on a location update (such as network activity) then you should
604     * select non-zero values for minTime and/or minDistance to rate-limit
605     * your update frequency in the case another application enables a
606     * location provider with extremely fast updates.
607     *
608     * <p>In case the provider is disabled by the user, updates will stop,
609     * and a provider availability update will be sent.
610     * As soon as the provider is enabled again,
611     * location updates will immediately resume and a provider availability
612     * update sent. Providers can also send status updates, at any time,
613     * with extra's specific to the provider. If a callback was supplied
614     * then status and availability updates are via
615     * {@link LocationListener#onProviderDisabled},
616     * {@link LocationListener#onProviderEnabled} or
617     * {@link LocationListener#onStatusChanged}. Alternately, if a
618     * pending intent was supplied then status and availability updates
619     * are broadcast intents with extra keys of
620     * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
621     *
622     * <p> If a {@link LocationListener} is used but with no Looper specified
623     * then the calling thread must already
624     * be a {@link android.os.Looper} thread such as the main thread of the
625     * calling Activity. If a Looper is specified with a {@link LocationListener}
626     * then callbacks are made on the supplied Looper thread.
627     *
628     * <p class="note"> Prior to Jellybean, the minTime parameter was
629     * only a hint, and some location provider implementations ignored it.
630     * From Jellybean and onwards it is mandatory for Android compatible
631     * devices to observe both the minTime and minDistance parameters.
632     *
633     * @param minTime minimum time interval between location updates, in milliseconds
634     * @param minDistance minimum distance between location updates, in meters
635     * @param criteria contains parameters for the location manager to choose the
636     * appropriate provider and parameters to compute the location
637     * @param intent a {@link PendingIntent} to be sent for each location update
638     *
639     * @throws IllegalArgumentException if criteria is null
640     * @throws IllegalArgumentException if intent is null
641     * @throws SecurityException if no suitable permission is present
642     */
643    public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
644            PendingIntent intent) {
645        checkCriteria(criteria);
646        checkPendingIntent(intent);
647
648        LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
649                criteria, minTime, minDistance, false);
650        requestLocationUpdates(request, null, null, intent);
651    }
652
653    /**
654     * Register for a single location update using the named provider and
655     * a callback.
656     *
657     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
658     * for more detail on how to use this method.
659     *
660     * @param provider the name of the provider with which to register
661     * @param listener a {@link LocationListener} whose
662     * {@link LocationListener#onLocationChanged} method will be called when
663     * the location update is available
664     * @param looper a Looper object whose message queue will be used to
665     * implement the callback mechanism, or null to make callbacks on the calling
666     * thread
667     *
668     * @throws IllegalArgumentException if provider is null or doesn't exist
669     * @throws IllegalArgumentException if listener is null
670     * @throws SecurityException if no suitable permission is present
671     */
672    public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
673        checkProvider(provider);
674        checkListener(listener);
675
676        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
677                provider, 0, 0, true);
678        requestLocationUpdates(request, listener, looper, null);
679    }
680
681    /**
682     * Register for a single location update using a Criteria and
683     * a callback.
684     *
685     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
686     * for more detail on how to use this method.
687     *
688     * @param criteria contains parameters for the location manager to choose the
689     * appropriate provider and parameters to compute the location
690     * @param listener a {@link LocationListener} whose
691     * {@link LocationListener#onLocationChanged} method will be called when
692     * the location update is available
693     * @param looper a Looper object whose message queue will be used to
694     * implement the callback mechanism, or null to make callbacks on the calling
695     * thread
696     *
697     * @throws IllegalArgumentException if criteria is null
698     * @throws IllegalArgumentException if listener is null
699     * @throws SecurityException if no suitable permission is present
700     */
701    public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
702        checkCriteria(criteria);
703        checkListener(listener);
704
705        LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
706                criteria, 0, 0, true);
707        requestLocationUpdates(request, listener, looper, null);
708    }
709
710    /**
711     * Register for a single location update using a named provider and pending intent.
712     *
713     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
714     * for more detail on how to use this method.
715     *
716     * @param provider the name of the provider with which to register
717     * @param intent a {@link PendingIntent} to be sent for the location update
718     *
719     * @throws IllegalArgumentException if provider is null or doesn't exist
720     * @throws IllegalArgumentException if intent is null
721     * @throws SecurityException if no suitable permission is present
722     */
723    public void requestSingleUpdate(String provider, PendingIntent intent) {
724        checkProvider(provider);
725        checkPendingIntent(intent);
726
727        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
728                provider, 0, 0, true);
729        requestLocationUpdates(request, null, null, intent);
730    }
731
732    /**
733     * Register for a single location update using a Criteria and pending intent.
734     *
735     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
736     * for more detail on how to use this method.
737     *
738     * @param criteria contains parameters for the location manager to choose the
739     * appropriate provider and parameters to compute the location
740     * @param intent a {@link PendingIntent} to be sent for the location update
741     *
742     * @throws IllegalArgumentException if provider is null or doesn't exist
743     * @throws IllegalArgumentException if intent is null
744     * @throws SecurityException if no suitable permission is present
745     */
746    public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
747        checkCriteria(criteria);
748        checkPendingIntent(intent);
749
750        LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
751                criteria, 0, 0, true);
752        requestLocationUpdates(request, null, null, intent);
753    }
754
755    /**
756     * Register for fused location updates using a LocationRequest and callback.
757     *
758     * <p>Upon a location update, the system delivers the new {@link Location} to the
759     * provided {@link LocationListener}, by calling its {@link
760     * LocationListener#onLocationChanged} method.</p>
761     *
762     * <p>The system will automatically select and enable the best providers
763     * to compute a location for your application. It may use only passive
764     * locations, or just a single location source, or it may fuse together
765     * multiple location sources in order to produce the best possible
766     * result, depending on the quality of service requested in the
767     * {@link LocationRequest}.
768     *
769     * <p>LocationRequest can be null, in which case the system will choose
770     * default, low power parameters for location updates. You will occasionally
771     * receive location updates as available, without a major power impact on the
772     * system. If your application just needs an occasional location update
773     * without any strict demands, then pass a null LocationRequest.
774     *
775     * <p>Only one LocationRequest can be registered for each unique callback
776     * or pending intent. So a subsequent request with the same callback or
777     * pending intent will over-write the previous LocationRequest.
778     *
779     * <p> If a pending intent is supplied then location updates
780     * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
781     * {@link android.location.Location} value. If a callback is supplied
782     * then location updates are made using the
783     * {@link LocationListener#onLocationChanged} callback, on the specified
784     * Looper thread. If a {@link LocationListener} is used
785     * but with a null Looper then the calling thread must already
786     * be a {@link android.os.Looper} thread (such as the main thread) and
787     * callbacks will occur on this thread.
788     *
789     * <p> Provider status updates and availability updates are deprecated
790     * because the system is performing provider fusion on the applications
791     * behalf. So {@link LocationListener#onProviderDisabled},
792     * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
793     * will not be called, and intents with extra keys of
794     * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
795     * be received.
796     *
797     * <p> To unregister for Location updates, use: {@link #removeUpdates(LocationListener)}.
798     *
799     * @param request quality of service required, null for default low power
800     * @param listener a {@link LocationListener} whose
801     * {@link LocationListener#onLocationChanged} method will be called when
802     * the location update is available
803     * @param looper a Looper object whose message queue will be used to
804     * implement the callback mechanism, or null to make callbacks on the calling
805     * thread
806     *
807     * @throws IllegalArgumentException if listener is null
808     * @throws SecurityException if no suitable permission is present
809     *
810     * @hide
811     */
812    @SystemApi
813    public void requestLocationUpdates(LocationRequest request, LocationListener listener,
814            Looper looper) {
815        checkListener(listener);
816        requestLocationUpdates(request, listener, looper, null);
817    }
818
819
820    /**
821     * Register for fused location updates using a LocationRequest and a pending intent.
822     *
823     * <p>Upon a location update, the system delivers the new {@link Location} with your provided
824     * {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED}
825     * in the intent's extras.</p>
826     *
827     * <p> To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}.
828     *
829     * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
830     * for more detail.
831     *
832     * @param request quality of service required, null for default low power
833     * @param intent a {@link PendingIntent} to be sent for the location update
834     *
835     * @throws IllegalArgumentException if intent is null
836     * @throws SecurityException if no suitable permission is present
837     *
838     * @hide
839     */
840    @SystemApi
841    public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
842        checkPendingIntent(intent);
843        requestLocationUpdates(request, null, null, intent);
844    }
845
846    private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
847        if (listener == null) return null;
848        synchronized (mListeners) {
849            ListenerTransport transport = mListeners.get(listener);
850            if (transport == null) {
851                transport = new ListenerTransport(listener, looper);
852            }
853            mListeners.put(listener, transport);
854            return transport;
855        }
856    }
857
858    private void requestLocationUpdates(LocationRequest request, LocationListener listener,
859            Looper looper, PendingIntent intent) {
860
861        String packageName = mContext.getPackageName();
862
863        // wrap the listener class
864        ListenerTransport transport = wrapListener(listener, looper);
865
866        try {
867            mService.requestLocationUpdates(request, transport, intent, packageName);
868       } catch (RemoteException e) {
869           Log.e(TAG, "RemoteException", e);
870       }
871    }
872
873    /**
874     * Removes all location updates for the specified LocationListener.
875     *
876     * <p>Following this call, updates will no longer
877     * occur for this listener.
878     *
879     * @param listener listener object that no longer needs location updates
880     * @throws IllegalArgumentException if listener is null
881     */
882    public void removeUpdates(LocationListener listener) {
883        checkListener(listener);
884        String packageName = mContext.getPackageName();
885
886        ListenerTransport transport;
887        synchronized (mListeners) {
888            transport = mListeners.remove(listener);
889        }
890        if (transport == null) return;
891
892        try {
893            mService.removeUpdates(transport, null, packageName);
894        } catch (RemoteException e) {
895            Log.e(TAG, "RemoteException", e);
896        }
897    }
898
899    /**
900     * Removes all location updates for the specified pending intent.
901     *
902     * <p>Following this call, updates will no longer for this pending intent.
903     *
904     * @param intent pending intent object that no longer needs location updates
905     * @throws IllegalArgumentException if intent is null
906     */
907    public void removeUpdates(PendingIntent intent) {
908        checkPendingIntent(intent);
909        String packageName = mContext.getPackageName();
910
911        try {
912            mService.removeUpdates(null, intent, packageName);
913        } catch (RemoteException e) {
914            Log.e(TAG, "RemoteException", e);
915        }
916    }
917
918    /**
919     * Set a proximity alert for the location given by the position
920     * (latitude, longitude) and the given radius.
921     *
922     * <p> When the device
923     * detects that it has entered or exited the area surrounding the
924     * location, the given PendingIntent will be used to create an Intent
925     * to be fired.
926     *
927     * <p> The fired Intent will have a boolean extra added with key
928     * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
929     * entering the proximity region; if false, it is exiting.
930     *
931     * <p> Due to the approximate nature of position estimation, if the
932     * device passes through the given area briefly, it is possible
933     * that no Intent will be fired.  Similarly, an Intent could be
934     * fired if the device passes very close to the given area but
935     * does not actually enter it.
936     *
937     * <p> After the number of milliseconds given by the expiration
938     * parameter, the location manager will delete this proximity
939     * alert and no longer monitor it.  A value of -1 indicates that
940     * there should be no expiration time.
941     *
942     * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
943     * and {@link #GPS_PROVIDER}.
944     *
945     * <p>Before API version 17, this method could be used with
946     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
947     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
948     * From API version 17 and onwards, this method requires
949     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
950     *
951     * @param latitude the latitude of the central point of the
952     * alert region
953     * @param longitude the longitude of the central point of the
954     * alert region
955     * @param radius the radius of the central point of the
956     * alert region, in meters
957     * @param expiration time for this proximity alert, in milliseconds,
958     * or -1 to indicate no expiration
959     * @param intent a PendingIntent that will be used to generate an Intent to
960     * fire when entry to or exit from the alert region is detected
961     *
962     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
963     * permission is not present
964     */
965    public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
966            PendingIntent intent) {
967        checkPendingIntent(intent);
968        if (expiration < 0) expiration = Long.MAX_VALUE;
969
970        Geofence fence = Geofence.createCircle(latitude, longitude, radius);
971        LocationRequest request = new LocationRequest().setExpireIn(expiration);
972        try {
973            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
974        } catch (RemoteException e) {
975            Log.e(TAG, "RemoteException", e);
976        }
977    }
978
979    /**
980     * Add a geofence with the specified LocationRequest quality of service.
981     *
982     * <p> When the device
983     * detects that it has entered or exited the area surrounding the
984     * location, the given PendingIntent will be used to create an Intent
985     * to be fired.
986     *
987     * <p> The fired Intent will have a boolean extra added with key
988     * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
989     * entering the proximity region; if false, it is exiting.
990     *
991     * <p> The geofence engine fuses results from all location providers to
992     * provide the best balance between accuracy and power. Applications
993     * can choose the quality of service required using the
994     * {@link LocationRequest} object. If it is null then a default,
995     * low power geo-fencing implementation is used. It is possible to cross
996     * a geo-fence without notification, but the system will do its best
997     * to detect, using {@link LocationRequest} as a hint to trade-off
998     * accuracy and power.
999     *
1000     * <p> The power required by the geofence engine can depend on many factors,
1001     * such as quality and interval requested in {@link LocationRequest},
1002     * distance to nearest geofence and current device velocity.
1003     *
1004     * @param request quality of service required, null for default low power
1005     * @param fence a geographical description of the geofence area
1006     * @param intent pending intent to receive geofence updates
1007     *
1008     * @throws IllegalArgumentException if fence is null
1009     * @throws IllegalArgumentException if intent is null
1010     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1011     * permission is not present
1012     *
1013     * @hide
1014     */
1015    public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
1016        checkPendingIntent(intent);
1017        checkGeofence(fence);
1018
1019        try {
1020            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
1021        } catch (RemoteException e) {
1022            Log.e(TAG, "RemoteException", e);
1023        }
1024    }
1025
1026    /**
1027     * Removes the proximity alert with the given PendingIntent.
1028     *
1029     * <p>Before API version 17, this method could be used with
1030     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
1031     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
1032     * From API version 17 and onwards, this method requires
1033     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
1034     *
1035     * @param intent the PendingIntent that no longer needs to be notified of
1036     * proximity alerts
1037     *
1038     * @throws IllegalArgumentException if intent is null
1039     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1040     * permission is not present
1041     */
1042    public void removeProximityAlert(PendingIntent intent) {
1043        checkPendingIntent(intent);
1044        String packageName = mContext.getPackageName();
1045
1046        try {
1047            mService.removeGeofence(null, intent, packageName);
1048        } catch (RemoteException e) {
1049            Log.e(TAG, "RemoteException", e);
1050        }
1051    }
1052
1053    /**
1054     * Remove a single geofence.
1055     *
1056     * <p>This removes only the specified geofence associated with the
1057     * specified pending intent. All other geofences remain unchanged.
1058     *
1059     * @param fence a geofence previously passed to {@link #addGeofence}
1060     * @param intent a pending intent previously passed to {@link #addGeofence}
1061     *
1062     * @throws IllegalArgumentException if fence is null
1063     * @throws IllegalArgumentException if intent is null
1064     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1065     * permission is not present
1066     *
1067     * @hide
1068     */
1069    public void removeGeofence(Geofence fence, PendingIntent intent) {
1070        checkPendingIntent(intent);
1071        checkGeofence(fence);
1072        String packageName = mContext.getPackageName();
1073
1074        try {
1075            mService.removeGeofence(fence, intent, packageName);
1076        } catch (RemoteException e) {
1077            Log.e(TAG, "RemoteException", e);
1078        }
1079    }
1080
1081    /**
1082     * Remove all geofences registered to the specified pending intent.
1083     *
1084     * @param intent a pending intent previously passed to {@link #addGeofence}
1085     *
1086     * @throws IllegalArgumentException if intent is null
1087     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1088     * permission is not present
1089     *
1090     * @hide
1091     */
1092    public void removeAllGeofences(PendingIntent intent) {
1093        checkPendingIntent(intent);
1094        String packageName = mContext.getPackageName();
1095
1096        try {
1097            mService.removeGeofence(null, intent, packageName);
1098        } catch (RemoteException e) {
1099            Log.e(TAG, "RemoteException", e);
1100        }
1101    }
1102
1103    /**
1104     * Returns the current enabled/disabled status of the given provider.
1105     *
1106     * <p>If the user has enabled this provider in the Settings menu, true
1107     * is returned otherwise false is returned
1108     *
1109     * <p>Callers should instead use
1110     * {@link android.provider.Settings.Secure#LOCATION_MODE}
1111     * unless they depend on provider-specific APIs such as
1112     * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
1113     *
1114     * <p>
1115     * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
1116     * method would throw {@link SecurityException} if the location permissions
1117     * were not sufficient to use the specified provider.
1118     *
1119     * @param provider the name of the provider
1120     * @return true if the provider exists and is enabled
1121     *
1122     * @throws IllegalArgumentException if provider is null
1123     */
1124    public boolean isProviderEnabled(String provider) {
1125        checkProvider(provider);
1126
1127        try {
1128            return mService.isProviderEnabled(provider);
1129        } catch (RemoteException e) {
1130            Log.e(TAG, "RemoteException", e);
1131            return false;
1132        }
1133    }
1134
1135    /**
1136     * Get the last known location.
1137     *
1138     * <p>This location could be very old so use
1139     * {@link Location#getElapsedRealtimeNanos} to calculate its age. It can
1140     * also return null if no previous location is available.
1141     *
1142     * <p>Always returns immediately.
1143     *
1144     * @return The last known location, or null if not available
1145     * @throws SecurityException if no suitable permission is present
1146     *
1147     * @hide
1148     */
1149    public Location getLastLocation() {
1150        String packageName = mContext.getPackageName();
1151
1152        try {
1153            return mService.getLastLocation(null, packageName);
1154        } catch (RemoteException e) {
1155            Log.e(TAG, "RemoteException", e);
1156            return null;
1157        }
1158    }
1159
1160    /**
1161     * Returns a Location indicating the data from the last known
1162     * location fix obtained from the given provider.
1163     *
1164     * <p> This can be done
1165     * without starting the provider.  Note that this location could
1166     * be out-of-date, for example if the device was turned off and
1167     * moved to another location.
1168     *
1169     * <p> If the provider is currently disabled, null is returned.
1170     *
1171     * @param provider the name of the provider
1172     * @return the last known location for the provider, or null
1173     *
1174     * @throws SecurityException if no suitable permission is present
1175     * @throws IllegalArgumentException if provider is null or doesn't exist
1176     */
1177    public Location getLastKnownLocation(String provider) {
1178        checkProvider(provider);
1179        String packageName = mContext.getPackageName();
1180        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
1181                provider, 0, 0, true);
1182
1183        try {
1184            return mService.getLastLocation(request, packageName);
1185        } catch (RemoteException e) {
1186            Log.e(TAG, "RemoteException", e);
1187            return null;
1188        }
1189    }
1190
1191    // --- Mock provider support ---
1192    // TODO: It would be fantastic to deprecate mock providers entirely, and replace
1193    // with something closer to LocationProviderBase.java
1194
1195    /**
1196     * Creates a mock location provider and adds it to the set of active providers.
1197     *
1198     * @param name the provider name
1199     *
1200     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1201     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1202     * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
1203     * @throws IllegalArgumentException if a provider with the given name already exists
1204     */
1205    public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1206            boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1207            boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1208        ProviderProperties properties = new ProviderProperties(requiresNetwork,
1209                requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
1210                supportsBearing, powerRequirement, accuracy);
1211        if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
1212            throw new IllegalArgumentException("provider name contains illegal character: " + name);
1213        }
1214
1215        try {
1216            mService.addTestProvider(name, properties);
1217        } catch (RemoteException e) {
1218            Log.e(TAG, "RemoteException", e);
1219        }
1220    }
1221
1222    /**
1223     * Removes the mock location provider with the given name.
1224     *
1225     * @param provider the provider name
1226     *
1227     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1228     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1229     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1230     * @throws IllegalArgumentException if no provider with the given name exists
1231     */
1232    public void removeTestProvider(String provider) {
1233        try {
1234            mService.removeTestProvider(provider);
1235        } catch (RemoteException e) {
1236            Log.e(TAG, "RemoteException", e);
1237        }
1238    }
1239
1240    /**
1241     * Sets a mock location for the given provider.
1242     * <p>This location will be used in place of any actual location from the provider.
1243     * The location object must have a minimum number of fields set to be
1244     * considered a valid LocationProvider Location, as per documentation
1245     * on {@link Location} class.
1246     *
1247     * @param provider the provider name
1248     * @param loc the mock location
1249     *
1250     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1251     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1252     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1253     * @throws IllegalArgumentException if no provider with the given name exists
1254     * @throws IllegalArgumentException if the location is incomplete
1255     */
1256    public void setTestProviderLocation(String provider, Location loc) {
1257        if (!loc.isComplete()) {
1258            IllegalArgumentException e = new IllegalArgumentException(
1259                    "Incomplete location object, missing timestamp or accuracy? " + loc);
1260            if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
1261                // just log on old platform (for backwards compatibility)
1262                Log.w(TAG, e);
1263                loc.makeComplete();
1264            } else {
1265                // really throw it!
1266                throw e;
1267            }
1268        }
1269
1270        try {
1271            mService.setTestProviderLocation(provider, loc);
1272        } catch (RemoteException e) {
1273            Log.e(TAG, "RemoteException", e);
1274        }
1275    }
1276
1277    /**
1278     * Removes any mock location associated with the given provider.
1279     *
1280     * @param provider the provider name
1281     *
1282     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1283     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1284     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1285     * @throws IllegalArgumentException if no provider with the given name exists
1286     */
1287    public void clearTestProviderLocation(String provider) {
1288        try {
1289            mService.clearTestProviderLocation(provider);
1290        } catch (RemoteException e) {
1291            Log.e(TAG, "RemoteException", e);
1292        }
1293    }
1294
1295    /**
1296     * Sets a mock enabled value for the given provider.  This value will be used in place
1297     * of any actual value from the provider.
1298     *
1299     * @param provider the provider name
1300     * @param enabled the mock enabled value
1301     *
1302     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1303     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1304     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1305     * @throws IllegalArgumentException if no provider with the given name exists
1306     */
1307    public void setTestProviderEnabled(String provider, boolean enabled) {
1308        try {
1309            mService.setTestProviderEnabled(provider, enabled);
1310        } catch (RemoteException e) {
1311            Log.e(TAG, "RemoteException", e);
1312        }
1313    }
1314
1315    /**
1316     * Removes any mock enabled value associated with the given provider.
1317     *
1318     * @param provider the provider name
1319     *
1320     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1321     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1322     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1323     * @throws IllegalArgumentException if no provider with the given name exists
1324     */
1325    public void clearTestProviderEnabled(String provider) {
1326        try {
1327            mService.clearTestProviderEnabled(provider);
1328        } catch (RemoteException e) {
1329            Log.e(TAG, "RemoteException", e);
1330        }
1331    }
1332
1333    /**
1334     * Sets mock status values for the given provider.  These values will be used in place
1335     * of any actual values from the provider.
1336     *
1337     * @param provider the provider name
1338     * @param status the mock status
1339     * @param extras a Bundle containing mock extras
1340     * @param updateTime the mock update time
1341     *
1342     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1343     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1344     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1345     * @throws IllegalArgumentException if no provider with the given name exists
1346     */
1347    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1348        try {
1349            mService.setTestProviderStatus(provider, status, extras, updateTime);
1350        } catch (RemoteException e) {
1351            Log.e(TAG, "RemoteException", e);
1352        }
1353    }
1354
1355    /**
1356     * Removes any mock status values associated with the given provider.
1357     *
1358     * @param provider the provider name
1359     *
1360     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1361     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1362     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1363     * @throws IllegalArgumentException if no provider with the given name exists
1364     */
1365    public void clearTestProviderStatus(String provider) {
1366        try {
1367            mService.clearTestProviderStatus(provider);
1368        } catch (RemoteException e) {
1369            Log.e(TAG, "RemoteException", e);
1370        }
1371    }
1372
1373    // --- GPS-specific support ---
1374
1375    // This class is used to send GPS status events to the client's main thread.
1376    private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
1377
1378        private final GpsStatus.Listener mListener;
1379        private final GpsStatus.NmeaListener mNmeaListener;
1380
1381        // This must not equal any of the GpsStatus event IDs
1382        private static final int NMEA_RECEIVED = 1000;
1383
1384        private class Nmea {
1385            long mTimestamp;
1386            String mNmea;
1387
1388            Nmea(long timestamp, String nmea) {
1389                mTimestamp = timestamp;
1390                mNmea = nmea;
1391            }
1392        }
1393        private ArrayList<Nmea> mNmeaBuffer;
1394
1395        GpsStatusListenerTransport(GpsStatus.Listener listener) {
1396            mListener = listener;
1397            mNmeaListener = null;
1398        }
1399
1400        GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
1401            mNmeaListener = listener;
1402            mListener = null;
1403            mNmeaBuffer = new ArrayList<Nmea>();
1404        }
1405
1406        @Override
1407        public void onGpsStarted() {
1408            if (mListener != null) {
1409                Message msg = Message.obtain();
1410                msg.what = GpsStatus.GPS_EVENT_STARTED;
1411                mGpsHandler.sendMessage(msg);
1412            }
1413        }
1414
1415        @Override
1416        public void onGpsStopped() {
1417            if (mListener != null) {
1418                Message msg = Message.obtain();
1419                msg.what = GpsStatus.GPS_EVENT_STOPPED;
1420                mGpsHandler.sendMessage(msg);
1421            }
1422        }
1423
1424        @Override
1425        public void onFirstFix(int ttff) {
1426            if (mListener != null) {
1427                mGpsStatus.setTimeToFirstFix(ttff);
1428                Message msg = Message.obtain();
1429                msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
1430                mGpsHandler.sendMessage(msg);
1431            }
1432        }
1433
1434        @Override
1435        public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
1436                float[] elevations, float[] azimuths, int ephemerisMask,
1437                int almanacMask, int usedInFixMask) {
1438            if (mListener != null) {
1439                mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
1440                        ephemerisMask, almanacMask, usedInFixMask);
1441
1442                Message msg = Message.obtain();
1443                msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
1444                // remove any SV status messages already in the queue
1445                mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
1446                mGpsHandler.sendMessage(msg);
1447            }
1448        }
1449
1450        @Override
1451        public void onNmeaReceived(long timestamp, String nmea) {
1452            if (mNmeaListener != null) {
1453                synchronized (mNmeaBuffer) {
1454                    mNmeaBuffer.add(new Nmea(timestamp, nmea));
1455                }
1456                Message msg = Message.obtain();
1457                msg.what = NMEA_RECEIVED;
1458                // remove any NMEA_RECEIVED messages already in the queue
1459                mGpsHandler.removeMessages(NMEA_RECEIVED);
1460                mGpsHandler.sendMessage(msg);
1461            }
1462        }
1463
1464        private final Handler mGpsHandler = new Handler() {
1465            @Override
1466            public void handleMessage(Message msg) {
1467                if (msg.what == NMEA_RECEIVED) {
1468                    synchronized (mNmeaBuffer) {
1469                        int length = mNmeaBuffer.size();
1470                        for (int i = 0; i < length; i++) {
1471                            Nmea nmea = mNmeaBuffer.get(i);
1472                            mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
1473                        }
1474                        mNmeaBuffer.clear();
1475                    }
1476                } else {
1477                    // synchronize on mGpsStatus to ensure the data is copied atomically.
1478                    synchronized(mGpsStatus) {
1479                        mListener.onGpsStatusChanged(msg.what);
1480                    }
1481                }
1482            }
1483        };
1484    }
1485
1486    /**
1487     * Adds a GPS status listener.
1488     *
1489     * @param listener GPS status listener object to register
1490     *
1491     * @return true if the listener was successfully added
1492     *
1493     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
1494     */
1495    public boolean addGpsStatusListener(GpsStatus.Listener listener) {
1496        boolean result;
1497
1498        if (mGpsStatusListeners.get(listener) != null) {
1499            // listener is already registered
1500            return true;
1501        }
1502        try {
1503            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
1504            result = mService.addGpsStatusListener(transport, mContext.getPackageName());
1505            if (result) {
1506                mGpsStatusListeners.put(listener, transport);
1507            }
1508        } catch (RemoteException e) {
1509            Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
1510            result = false;
1511        }
1512
1513        return result;
1514    }
1515
1516    /**
1517     * Removes a GPS status listener.
1518     *
1519     * @param listener GPS status listener object to remove
1520     */
1521    public void removeGpsStatusListener(GpsStatus.Listener listener) {
1522        try {
1523            GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
1524            if (transport != null) {
1525                mService.removeGpsStatusListener(transport);
1526            }
1527        } catch (RemoteException e) {
1528            Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
1529        }
1530    }
1531
1532    /**
1533     * Adds an NMEA listener.
1534     *
1535     * @param listener a {@link GpsStatus.NmeaListener} object to register
1536     *
1537     * @return true if the listener was successfully added
1538     *
1539     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
1540     */
1541    public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
1542        boolean result;
1543
1544        if (mNmeaListeners.get(listener) != null) {
1545            // listener is already registered
1546            return true;
1547        }
1548        try {
1549            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
1550            result = mService.addGpsStatusListener(transport, mContext.getPackageName());
1551            if (result) {
1552                mNmeaListeners.put(listener, transport);
1553            }
1554        } catch (RemoteException e) {
1555            Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
1556            result = false;
1557        }
1558
1559        return result;
1560    }
1561
1562    /**
1563     * Removes an NMEA listener.
1564     *
1565     * @param listener a {@link GpsStatus.NmeaListener} object to remove
1566     */
1567    public void removeNmeaListener(GpsStatus.NmeaListener listener) {
1568        try {
1569            GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
1570            if (transport != null) {
1571                mService.removeGpsStatusListener(transport);
1572            }
1573        } catch (RemoteException e) {
1574            Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
1575        }
1576    }
1577
1578    /**
1579     * Adds a GPS Measurement listener.
1580     *
1581     * @param listener a {@link GpsMeasurementsEvent.Listener} object to register.
1582     * @return {@code true} if the listener was added successfully, {@code false} otherwise.
1583     *
1584     * @hide
1585     */
1586    @SystemApi
1587    public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
1588        return mGpsMeasurementListenerTransport.add(listener);
1589    }
1590
1591    /**
1592     * Removes a GPS Measurement listener.
1593     *
1594     * @param listener a {@link GpsMeasurementsEvent.Listener} object to remove.
1595     *
1596     * @hide
1597     */
1598    @SystemApi
1599    public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
1600        mGpsMeasurementListenerTransport.remove(listener);
1601    }
1602
1603    /**
1604     * Adds a GPS Navigation Message listener.
1605     *
1606     * @param listener a {@link GpsNavigationMessageEvent.Listener} object to register.
1607     * @return {@code true} if the listener was added successfully, {@code false} otherwise.
1608     *
1609     * @hide
1610     */
1611    @SystemApi
1612    public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
1613        return mGpsNavigationMessageListenerTransport.add(listener);
1614    }
1615
1616    /**
1617     * Removes a GPS Navigation Message listener.
1618     *
1619     * @param listener a {@link GpsNavigationMessageEvent.Listener} object to remove.
1620     *
1621     * @hide
1622     */
1623    @SystemApi
1624    public void removeGpsNavigationMessageListener(
1625            GpsNavigationMessageEvent.Listener listener) {
1626        mGpsNavigationMessageListenerTransport.remove(listener);
1627    }
1628
1629     /**
1630     * Retrieves information about the current status of the GPS engine.
1631     * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
1632     * callback to ensure that the data is copied atomically.
1633     *
1634     * The caller may either pass in a {@link GpsStatus} object to set with the latest
1635     * status information, or pass null to create a new {@link GpsStatus} object.
1636     *
1637     * @param status object containing GPS status details, or null.
1638     * @return status object containing updated GPS status.
1639     */
1640    public GpsStatus getGpsStatus(GpsStatus status) {
1641        if (status == null) {
1642            status = new GpsStatus();
1643        }
1644        status.setStatus(mGpsStatus);
1645        return status;
1646    }
1647
1648    /**
1649     * Sends additional commands to a location provider.
1650     * Can be used to support provider specific extensions to the Location Manager API
1651     *
1652     * @param provider name of the location provider.
1653     * @param command name of the command to send to the provider.
1654     * @param extras optional arguments for the command (or null).
1655     * The provider may optionally fill the extras Bundle with results from the command.
1656     *
1657     * @return true if the command succeeds.
1658     */
1659    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1660        try {
1661            return mService.sendExtraCommand(provider, command, extras);
1662        } catch (RemoteException e) {
1663            Log.e(TAG, "RemoteException in sendExtraCommand: ", e);
1664            return false;
1665        }
1666    }
1667
1668    /**
1669     * Used by NetInitiatedActivity to report user response
1670     * for network initiated GPS fix requests.
1671     *
1672     * @hide
1673     */
1674    public boolean sendNiResponse(int notifId, int userResponse) {
1675        try {
1676            return mService.sendNiResponse(notifId, userResponse);
1677        } catch (RemoteException e) {
1678            Log.e(TAG, "RemoteException in sendNiResponse: ", e);
1679            return false;
1680        }
1681    }
1682
1683    private static void checkProvider(String provider) {
1684        if (provider == null) {
1685            throw new IllegalArgumentException("invalid provider: " + provider);
1686        }
1687    }
1688
1689    private static void checkCriteria(Criteria criteria) {
1690        if (criteria == null) {
1691            throw new IllegalArgumentException("invalid criteria: " + criteria);
1692        }
1693    }
1694
1695    private static void checkListener(LocationListener listener) {
1696        if (listener == null) {
1697            throw new IllegalArgumentException("invalid listener: " + listener);
1698        }
1699    }
1700
1701    private void checkPendingIntent(PendingIntent intent) {
1702        if (intent == null) {
1703            throw new IllegalArgumentException("invalid pending intent: " + intent);
1704        }
1705        if (!intent.isTargetedToPackage()) {
1706            IllegalArgumentException e = new IllegalArgumentException(
1707                    "pending intent must be targeted to package");
1708            if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
1709                throw e;
1710            } else {
1711                Log.w(TAG, e);
1712            }
1713        }
1714    }
1715
1716    private static void checkGeofence(Geofence fence) {
1717        if (fence == null) {
1718            throw new IllegalArgumentException("invalid geofence: " + fence);
1719        }
1720    }
1721}
1722