LocationManagerService.java revision a0e3cd3853da1d14da4918c49d25128622cf0610
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 com.android.server;
18
19import java.io.BufferedReader;
20import java.io.File;
21import java.io.FileDescriptor;
22import java.io.FileReader;
23import java.io.FileWriter;
24import java.io.IOException;
25import java.io.PrintWriter;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.List;
30import java.util.Map;
31import java.util.Observable;
32import java.util.Observer;
33import java.util.Set;
34import java.util.regex.Pattern;
35
36import android.app.AlarmManager;
37import android.app.PendingIntent;
38import android.content.BroadcastReceiver;
39import android.content.ContentQueryMap;
40import android.content.ContentResolver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
44import android.content.pm.PackageManager;
45import android.database.Cursor;
46import android.location.Address;
47import android.location.IGeocodeProvider;
48import android.location.IGpsStatusListener;
49import android.location.ILocationCollector;
50import android.location.ILocationListener;
51import android.location.ILocationManager;
52import android.location.ILocationProvider;
53import android.location.Location;
54import android.location.LocationManager;
55import android.location.LocationProvider;
56import android.location.LocationProviderImpl;
57import android.net.ConnectivityManager;
58import android.net.Uri;
59import android.os.Binder;
60import android.os.Bundle;
61import android.os.Handler;
62import android.os.IBinder;
63import android.os.Message;
64import android.os.PowerManager;
65import android.os.Process;
66import android.os.RemoteException;
67import android.os.SystemClock;
68import android.provider.Settings;
69import android.util.Config;
70import android.util.Log;
71import android.util.PrintWriterPrinter;
72import android.util.SparseIntArray;
73
74import com.android.internal.location.GpsLocationProvider;
75import com.android.internal.location.LocationProviderProxy;
76import com.android.internal.location.MockProvider;
77import com.android.internal.location.TrackProvider;
78import com.android.server.am.BatteryStatsService;
79
80/**
81 * The service class that manages LocationProviders and issues location
82 * updates and alerts.
83 *
84 * {@hide}
85 */
86public class LocationManagerService extends ILocationManager.Stub {
87    private static final String TAG = "LocationManagerService";
88    private static final boolean LOCAL_LOGV = false;
89
90    // Minimum time interval between last known location writes, in milliseconds.
91    private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
92
93    // Max time to hold wake lock for, in milliseconds.
94    private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
95
96    // Time to wait after releasing a wake lock for clients to process location update,
97    // in milliseconds.
98    private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
99
100    // The last time a location was written, by provider name.
101    private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
102
103    private static final Pattern PATTERN_COMMA = Pattern.compile(",");
104
105    private static final String ACCESS_FINE_LOCATION =
106        android.Manifest.permission.ACCESS_FINE_LOCATION;
107    private static final String ACCESS_COARSE_LOCATION =
108        android.Manifest.permission.ACCESS_COARSE_LOCATION;
109    private static final String ACCESS_MOCK_LOCATION =
110        android.Manifest.permission.ACCESS_MOCK_LOCATION;
111    private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
112        android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
113
114    // Set of providers that are explicitly enabled
115    private final Set<String> mEnabledProviders = new HashSet<String>();
116
117    // Set of providers that are explicitly disabled
118    private final Set<String> mDisabledProviders = new HashSet<String>();
119
120    // Locations, status values, and extras for mock providers
121    private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
122
123    private static boolean sProvidersLoaded = false;
124
125    private final Context mContext;
126    private GpsLocationProvider mGpsLocationProvider;
127    private LocationProviderProxy mNetworkLocationProvider;
128    private IGeocodeProvider mGeocodeProvider;
129    private LocationWorkerHandler mLocationHandler;
130
131    // Handler messages
132    private static final int MESSAGE_LOCATION_CHANGED = 1;
133    private static final int MESSAGE_RELEASE_WAKE_LOCK = 2;
134
135    // Alarm manager and wakelock variables
136    private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
137    private final static String WAKELOCK_KEY = "LocationManagerService";
138    private AlarmManager mAlarmManager;
139    private long mAlarmInterval = 0;
140    private PowerManager.WakeLock mWakeLock = null;
141    private long mWakeLockAcquireTime = 0;
142    private boolean mWakeLockGpsReceived = true;
143    private boolean mWakeLockNetworkReceived = true;
144
145    /**
146     * List of all receivers.
147     */
148    private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
149
150    /**
151     * Object used internally for synchronization
152     */
153    private final Object mLock = new Object();
154
155    /**
156     * Mapping from provider name to all its UpdateRecords
157     */
158    private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
159        new HashMap<String,ArrayList<UpdateRecord>>();
160
161    // Proximity listeners
162    private Receiver mProximityListener = null;
163    private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
164        new HashMap<PendingIntent,ProximityAlert>();
165    private HashSet<ProximityAlert> mProximitiesEntered =
166        new HashSet<ProximityAlert>();
167
168    // Last known location for each provider
169    private HashMap<String,Location> mLastKnownLocation =
170        new HashMap<String,Location>();
171
172    // Location collector
173    private ILocationCollector mCollector;
174
175    private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
176
177    // for Settings change notification
178    private ContentQueryMap mSettings;
179
180    /**
181     * A wrapper class holding either an ILocationListener or a PendingIntent to receive
182     * location updates.
183     */
184    private final class Receiver implements IBinder.DeathRecipient {
185        final ILocationListener mListener;
186        final PendingIntent mPendingIntent;
187        final Object mKey;
188        final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
189
190        Receiver(ILocationListener listener) {
191            mListener = listener;
192            mPendingIntent = null;
193            mKey = listener.asBinder();
194        }
195
196        Receiver(PendingIntent intent) {
197            mPendingIntent = intent;
198            mListener = null;
199            mKey = intent;
200        }
201
202        @Override
203        public boolean equals(Object otherObj) {
204            if (otherObj instanceof Receiver) {
205                return mKey.equals(
206                        ((Receiver)otherObj).mKey);
207            }
208            return false;
209        }
210
211        @Override
212        public int hashCode() {
213            return mKey.hashCode();
214        }
215
216
217        @Override
218        public String toString() {
219            if (mListener != null) {
220                return "Receiver{"
221                        + Integer.toHexString(System.identityHashCode(this))
222                        + " Listener " + mKey + "}";
223            } else {
224                return "Receiver{"
225                        + Integer.toHexString(System.identityHashCode(this))
226                        + " Intent " + mKey + "}";
227            }
228        }
229
230        public boolean isListener() {
231            return mListener != null;
232        }
233
234        public boolean isPendingIntent() {
235            return mPendingIntent != null;
236        }
237
238        public ILocationListener getListener() {
239            if (mListener != null) {
240                return mListener;
241            }
242            throw new IllegalStateException("Request for non-existent listener");
243        }
244
245        public PendingIntent getPendingIntent() {
246            if (mPendingIntent != null) {
247                return mPendingIntent;
248            }
249            throw new IllegalStateException("Request for non-existent intent");
250        }
251
252        public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
253            if (mListener != null) {
254                try {
255                    mListener.onStatusChanged(provider, status, extras);
256                } catch (RemoteException e) {
257                    return false;
258                }
259            } else {
260                Intent statusChanged = new Intent();
261                statusChanged.putExtras(extras);
262                statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
263                try {
264                    mPendingIntent.send(mContext, 0, statusChanged, null, null);
265                } catch (PendingIntent.CanceledException e) {
266                    return false;
267                }
268            }
269            return true;
270        }
271
272        public boolean callLocationChangedLocked(Location location) {
273            if (mListener != null) {
274                try {
275                    mListener.onLocationChanged(location);
276                } catch (RemoteException e) {
277                    return false;
278                }
279            } else {
280                Intent locationChanged = new Intent();
281                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
282                try {
283                    mPendingIntent.send(mContext, 0, locationChanged, null, null);
284                } catch (PendingIntent.CanceledException e) {
285                    return false;
286                }
287            }
288            return true;
289        }
290
291        public void binderDied() {
292            if (LOCAL_LOGV) {
293                Log.v(TAG, "Location listener died");
294            }
295            synchronized (mLock) {
296                removeUpdatesLocked(this);
297            }
298        }
299    }
300
301    private final class SettingsObserver implements Observer {
302        public void update(Observable o, Object arg) {
303            synchronized (mLock) {
304                updateProvidersLocked();
305            }
306        }
307    }
308
309    private Location readLastKnownLocationLocked(String provider) {
310        Location location = null;
311        String s = null;
312        try {
313            File f = new File(LocationManager.SYSTEM_DIR + "/location."
314                    + provider);
315            if (!f.exists()) {
316                return null;
317            }
318            BufferedReader reader = new BufferedReader(new FileReader(f), 256);
319            s = reader.readLine();
320        } catch (IOException e) {
321            Log.w(TAG, "Unable to read last known location", e);
322        }
323
324        if (s == null) {
325            return null;
326        }
327        try {
328            String[] tokens = PATTERN_COMMA.split(s);
329            int idx = 0;
330            long time = Long.parseLong(tokens[idx++]);
331            double latitude = Double.parseDouble(tokens[idx++]);
332            double longitude = Double.parseDouble(tokens[idx++]);
333            double altitude = Double.parseDouble(tokens[idx++]);
334            float bearing = Float.parseFloat(tokens[idx++]);
335            float speed = Float.parseFloat(tokens[idx++]);
336
337            location = new Location(provider);
338            location.setTime(time);
339            location.setLatitude(latitude);
340            location.setLongitude(longitude);
341            location.setAltitude(altitude);
342            location.setBearing(bearing);
343            location.setSpeed(speed);
344        } catch (NumberFormatException nfe) {
345            Log.e(TAG, "NumberFormatException reading last known location", nfe);
346            return null;
347        }
348
349        return location;
350    }
351
352    private void writeLastKnownLocationLocked(String provider,
353        Location location) {
354        long now = SystemClock.elapsedRealtime();
355        Long last = mLastWriteTime.get(provider);
356        if ((last != null)
357            && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
358            return;
359        }
360        mLastWriteTime.put(provider, now);
361
362        StringBuilder sb = new StringBuilder(100);
363        sb.append(location.getTime());
364        sb.append(',');
365        sb.append(location.getLatitude());
366        sb.append(',');
367        sb.append(location.getLongitude());
368        sb.append(',');
369        sb.append(location.getAltitude());
370        sb.append(',');
371        sb.append(location.getBearing());
372        sb.append(',');
373        sb.append(location.getSpeed());
374
375        FileWriter writer = null;
376        try {
377            File d = new File(LocationManager.SYSTEM_DIR);
378            if (!d.exists()) {
379                if (!d.mkdirs()) {
380                    Log.w(TAG, "Unable to create directory to write location");
381                    return;
382                }
383            }
384            File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
385            writer = new FileWriter(f);
386            writer.write(sb.toString());
387        } catch (IOException e) {
388            Log.w(TAG, "Unable to write location", e);
389        } finally {
390            if (writer != null) {
391                try {
392                writer.close();
393                } catch (IOException e) {
394                    Log.w(TAG, "Exception closing file", e);
395                }
396            }
397        }
398    }
399
400    /**
401     * Load providers from /data/location/<provider_name>/
402     *                                                          class
403     *                                                          kml
404     *                                                          nmea
405     *                                                          track
406     *                                                          location
407     *                                                          properties
408     */
409    private void loadProviders() {
410        synchronized (mLock) {
411            if (sProvidersLoaded) {
412                return;
413            }
414
415            // Load providers
416            loadProvidersLocked();
417            sProvidersLoaded = true;
418        }
419    }
420
421    private void loadProvidersLocked() {
422        try {
423            _loadProvidersLocked();
424        } catch (Exception e) {
425            Log.e(TAG, "Exception loading providers:", e);
426        }
427    }
428
429    private void _loadProvidersLocked() {
430        // Attempt to load "real" providers first
431        if (GpsLocationProvider.isSupported()) {
432            // Create a gps location provider
433            mGpsLocationProvider = new GpsLocationProvider(mContext, this);
434            LocationProviderImpl.addProvider(mGpsLocationProvider);
435        }
436
437        // Load fake providers if real providers are not available
438        File f = new File(LocationManager.PROVIDER_DIR);
439        if (f.isDirectory()) {
440            File[] subdirs = f.listFiles();
441            for (int i = 0; i < subdirs.length; i++) {
442                if (!subdirs[i].isDirectory()) {
443                    continue;
444                }
445
446                String name = subdirs[i].getName();
447
448                if (LOCAL_LOGV) {
449                    Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
450                    Log.v(TAG, "name = " + name);
451                }
452
453                // Don't create a fake provider if a real provider exists
454                if (LocationProviderImpl.getProvider(name) == null) {
455                    LocationProviderImpl provider = null;
456                    try {
457                        File classFile = new File(subdirs[i], "class");
458                        // Look for a 'class' file
459                        provider = LocationProviderImpl.loadFromClass(classFile);
460
461                        // Look for an 'kml', 'nmea', or 'track' file
462                        if (provider == null) {
463                            // Load properties from 'properties' file, if present
464                            File propertiesFile = new File(subdirs[i], "properties");
465
466                            if (propertiesFile.exists()) {
467                                provider = new TrackProvider(name, this);
468                                ((TrackProvider)provider).readProperties(propertiesFile);
469
470                                File kmlFile = new File(subdirs[i], "kml");
471                                if (kmlFile.exists()) {
472                                    ((TrackProvider) provider).readKml(kmlFile);
473                                } else {
474                                    File nmeaFile = new File(subdirs[i], "nmea");
475                                    if (nmeaFile.exists()) {
476                                        ((TrackProvider) provider).readNmea(name, nmeaFile);
477                                    } else {
478                                        File trackFile = new File(subdirs[i], "track");
479                                        if (trackFile.exists()) {
480                                            ((TrackProvider) provider).readTrack(trackFile);
481                                        }
482                                    }
483                                }
484                            }
485                        }
486                        if (provider != null) {
487                            LocationProviderImpl.addProvider(provider);
488                        }
489                        // Grab the initial location of a TrackProvider and
490                        // store it as the last known location for that provider
491                        if (provider instanceof TrackProvider) {
492                            TrackProvider tp = (TrackProvider) provider;
493                            mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
494                        }
495                    } catch (Exception e) {
496                        Log.e(TAG, "Exception loading provder " + name, e);
497                    }
498                }
499            }
500        }
501
502        updateProvidersLocked();
503    }
504
505    /**
506     * @param context the context that the LocationManagerService runs in
507     */
508    public LocationManagerService(Context context) {
509        super();
510        mContext = context;
511        mLocationHandler = new LocationWorkerHandler();
512
513        if (LOCAL_LOGV) {
514            Log.v(TAG, "Constructed LocationManager Service");
515        }
516
517        // Alarm manager, needs to be done before calling loadProviders() below
518        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
519
520        // Create a wake lock, needs to be done before calling loadProviders() below
521        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
522        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
523
524        // Load providers
525        loadProviders();
526
527        // Register for Network (Wifi or Mobile) updates
528        NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
529        IntentFilter networkIntentFilter = new IntentFilter();
530        networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
531        networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
532        context.registerReceiver(networkReceiver, networkIntentFilter);
533
534        // Register for power updates
535        PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
536        IntentFilter intentFilter = new IntentFilter();
537        intentFilter.addAction(ALARM_INTENT);
538        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
539        intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
540        context.registerReceiver(powerStateReceiver, intentFilter);
541
542        // listen for settings changes
543        ContentResolver resolver = mContext.getContentResolver();
544        Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
545                "(" + Settings.System.NAME + "=?)",
546                new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
547                null);
548        mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
549        SettingsObserver settingsObserver = new SettingsObserver();
550        mSettings.addObserver(settingsObserver);
551    }
552
553    public void setNetworkLocationProvider(ILocationProvider provider) {
554        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
555            throw new SecurityException(
556                "Installing location providers outside of the system is not supported");
557        }
558
559        synchronized (mLock) {
560            mNetworkLocationProvider =
561                    new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, this, provider);
562            LocationProviderImpl.addProvider(mNetworkLocationProvider);
563            updateProvidersLocked();
564
565            // notify NetworkLocationProvider of any events it might have missed
566            mNetworkLocationProvider.updateNetworkState(mNetworkState);
567        }
568    }
569
570    public void setLocationCollector(ILocationCollector collector) {
571        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
572            throw new SecurityException(
573                "Installing location collectors outside of the system is not supported");
574        }
575
576        mCollector = collector;
577    }
578
579    public void setGeocodeProvider(IGeocodeProvider provider) {
580        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
581            throw new SecurityException(
582                "Installing location providers outside of the system is not supported");
583        }
584
585        mGeocodeProvider = provider;
586    }
587
588    private boolean isAllowedBySettingsLocked(String provider) {
589        if (mEnabledProviders.contains(provider)) {
590            return true;
591        }
592        if (mDisabledProviders.contains(provider)) {
593            return false;
594        }
595        // Use system settings
596        ContentResolver resolver = mContext.getContentResolver();
597        String allowedProviders = Settings.Secure.getString(resolver,
598           Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
599
600        return ((allowedProviders != null) && (allowedProviders.contains(provider)));
601    }
602
603    private void checkPermissionsSafe(String provider) {
604        if (LocationManager.GPS_PROVIDER.equals(provider)
605            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
606                != PackageManager.PERMISSION_GRANTED)) {
607            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
608        }
609        if (LocationManager.NETWORK_PROVIDER.equals(provider)
610            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
611                != PackageManager.PERMISSION_GRANTED)
612            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
613                != PackageManager.PERMISSION_GRANTED)) {
614            throw new SecurityException(
615                "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
616        }
617    }
618
619    private boolean isAllowedProviderSafe(String provider) {
620        if (LocationManager.GPS_PROVIDER.equals(provider)
621            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
622                != PackageManager.PERMISSION_GRANTED)) {
623            return false;
624        }
625        if (LocationManager.NETWORK_PROVIDER.equals(provider)
626            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
627                != PackageManager.PERMISSION_GRANTED)
628            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
629                != PackageManager.PERMISSION_GRANTED)) {
630            return false;
631        }
632
633        return true;
634    }
635
636    public List<String> getAllProviders() {
637        try {
638            synchronized (mLock) {
639                return _getAllProvidersLocked();
640            }
641        } catch (SecurityException se) {
642            throw se;
643        } catch (Exception e) {
644            Log.e(TAG, "getAllProviders got exception:", e);
645            return null;
646        }
647    }
648
649    private List<String> _getAllProvidersLocked() {
650        if (LOCAL_LOGV) {
651            Log.v(TAG, "getAllProviders");
652        }
653        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
654        ArrayList<String> out = new ArrayList<String>(providers.size());
655
656        for (LocationProviderImpl p : providers) {
657            out.add(p.getName());
658        }
659        return out;
660    }
661
662    public List<String> getProviders(boolean enabledOnly) {
663        try {
664            synchronized (mLock) {
665                return _getProvidersLocked(enabledOnly);
666            }
667        } catch (SecurityException se) {
668            throw se;
669        } catch (Exception e) {
670            Log.e(TAG, "getProviders got exception:", e);
671            return null;
672        }
673    }
674
675    private List<String> _getProvidersLocked(boolean enabledOnly) {
676        if (LOCAL_LOGV) {
677            Log.v(TAG, "getProviders");
678        }
679        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
680        ArrayList<String> out = new ArrayList<String>();
681
682        for (LocationProviderImpl p : providers) {
683            String name = p.getName();
684            if (isAllowedProviderSafe(name)) {
685                if (enabledOnly && !isAllowedBySettingsLocked(name)) {
686                    continue;
687                }
688                out.add(name);
689            }
690        }
691        return out;
692    }
693
694    private void updateProvidersLocked() {
695        for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
696            boolean isEnabled = p.isEnabled();
697            String name = p.getName();
698            boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
699
700            if (isEnabled && !shouldBeEnabled) {
701                updateProviderListenersLocked(name, false);
702            } else if (!isEnabled && shouldBeEnabled) {
703                updateProviderListenersLocked(name, true);
704            }
705
706        }
707    }
708
709    private void updateProviderListenersLocked(String provider, boolean enabled) {
710        int listeners = 0;
711
712        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
713        if (p == null) {
714            return;
715        }
716
717        ArrayList<Receiver> deadReceivers = null;
718
719        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
720        if (records != null) {
721            final int N = records.size();
722            for (int i=0; i<N; i++) {
723                UpdateRecord record = records.get(i);
724                // Sends a notification message to the receiver
725                try {
726                    Receiver receiver = record.mReceiver;
727                    if (receiver.isListener()) {
728                        if (enabled) {
729                            receiver.getListener().onProviderEnabled(provider);
730                        } else {
731                            receiver.getListener().onProviderDisabled(provider);
732                        }
733                    } else {
734                        Intent providerIntent = new Intent();
735                        providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
736                        try {
737                            receiver.getPendingIntent().send(mContext, 0,
738                                 providerIntent, null, null);
739                        } catch (PendingIntent.CanceledException e) {
740                            if (deadReceivers == null) {
741                                deadReceivers = new ArrayList<Receiver>();
742                                deadReceivers.add(receiver);
743                            }
744                        }
745                    }
746                } catch (RemoteException e) {
747                    // The death link will clean this up.
748                }
749                listeners++;
750            }
751        }
752
753        if (deadReceivers != null) {
754            for (int i=deadReceivers.size()-1; i>=0; i--) {
755                removeUpdatesLocked(deadReceivers.get(i));
756            }
757        }
758
759        if (enabled) {
760            p.enable();
761            if (listeners > 0) {
762                p.setMinTime(getMinTimeLocked(provider));
763                p.enableLocationTracking(true);
764                updateWakelockStatusLocked();
765            }
766        } else {
767            p.enableLocationTracking(false);
768            p.disable();
769            updateWakelockStatusLocked();
770        }
771    }
772
773    private long getMinTimeLocked(String provider) {
774        long minTime = Long.MAX_VALUE;
775        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
776        if (records != null) {
777            for (int i=records.size()-1; i>=0; i--) {
778                minTime = Math.min(minTime, records.get(i).mMinTime);
779            }
780        }
781        return minTime;
782    }
783
784    private class UpdateRecord {
785        final String mProvider;
786        final Receiver mReceiver;
787        final long mMinTime;
788        final float mMinDistance;
789        final int mUid;
790        Location mLastFixBroadcast;
791        long mLastStatusBroadcast;
792
793        /**
794         * Note: must be constructed with lock held.
795         */
796        UpdateRecord(String provider, long minTime, float minDistance,
797            Receiver receiver, int uid) {
798            mProvider = provider;
799            mReceiver = receiver;
800            mMinTime = minTime;
801            mMinDistance = minDistance;
802            mUid = uid;
803
804            ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
805            if (records == null) {
806                records = new ArrayList<UpdateRecord>();
807                mRecordsByProvider.put(provider, records);
808            }
809            if (!records.contains(this)) {
810                records.add(this);
811            }
812        }
813
814        /**
815         * Method to be called when a record will no longer be used.  Calling this multiple times
816         * must have the same effect as calling it once.
817         */
818        void disposeLocked() {
819            ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
820            records.remove(this);
821        }
822
823        @Override
824        public String toString() {
825            return "UpdateRecord{"
826                    + Integer.toHexString(System.identityHashCode(this))
827                    + " " + mProvider + " " + mReceiver + "}";
828        }
829
830        void dump(PrintWriter pw, String prefix) {
831            pw.println(prefix + this);
832            pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
833            pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
834            pw.println(prefix + "mUid=" + mUid);
835            pw.println(prefix + "mLastFixBroadcast:");
836            mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + "  ");
837            pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
838        }
839
840        /**
841         * Calls dispose().
842         */
843        @Override protected void finalize() {
844            synchronized (mLock) {
845                disposeLocked();
846            }
847        }
848    }
849
850    private Receiver getReceiver(ILocationListener listener) {
851        IBinder binder = listener.asBinder();
852        Receiver receiver = mReceivers.get(binder);
853        if (receiver == null) {
854            receiver = new Receiver(listener);
855            mReceivers.put(binder, receiver);
856
857            try {
858                if (receiver.isListener()) {
859                    receiver.getListener().asBinder().linkToDeath(receiver, 0);
860                }
861            } catch (RemoteException e) {
862                Log.e(TAG, "linkToDeath failed:", e);
863                return null;
864            }
865        }
866        return receiver;
867    }
868
869    private Receiver getReceiver(PendingIntent intent) {
870        Receiver receiver = mReceivers.get(intent);
871        if (receiver == null) {
872            receiver = new Receiver(intent);
873            mReceivers.put(intent, receiver);
874        }
875        return receiver;
876    }
877
878    private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
879        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
880        if (records != null) {
881            for (int i = records.size() - 1; i >= 0; i--) {
882                UpdateRecord record = records.get(i);
883                if (record.mUid == uid && record.mReceiver != excludedReceiver) {
884                    return true;
885                }
886           }
887        }
888        if (LocationManager.GPS_PROVIDER.equals(provider) ||
889                LocationManager.NETWORK_PROVIDER.equals(provider)) {
890            for (ProximityAlert alert : mProximityAlerts.values()) {
891                if (alert.mUid == uid) {
892                    return true;
893                }
894            }
895        }
896        return false;
897    }
898
899    public void requestLocationUpdates(String provider,
900        long minTime, float minDistance, ILocationListener listener) {
901
902        try {
903            synchronized (mLock) {
904                requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
905            }
906        } catch (SecurityException se) {
907            throw se;
908        } catch (Exception e) {
909            Log.e(TAG, "requestUpdates got exception:", e);
910        }
911    }
912
913    public void requestLocationUpdatesPI(String provider,
914            long minTime, float minDistance, PendingIntent intent) {
915        try {
916            synchronized (mLock) {
917                requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
918            }
919        } catch (SecurityException se) {
920            throw se;
921        } catch (Exception e) {
922            Log.e(TAG, "requestUpdates got exception:", e);
923        }
924    }
925
926    private void requestLocationUpdatesLocked(String provider,
927            long minTime, float minDistance, Receiver receiver) {
928        if (LOCAL_LOGV) {
929            Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
930        }
931
932        LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
933        if (impl == null) {
934            throw new IllegalArgumentException("provider=" + provider);
935        }
936
937        checkPermissionsSafe(provider);
938
939        // so wakelock calls will succeed
940        final int callingUid = Binder.getCallingUid();
941        boolean newUid = !providerHasListener(provider, callingUid, null);
942        long identity = Binder.clearCallingIdentity();
943        try {
944            UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
945            UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
946            if (oldRecord != null) {
947                oldRecord.disposeLocked();
948            }
949
950            if (newUid) {
951                impl.addListener(callingUid);
952            }
953
954            boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
955            if (isProviderEnabled) {
956                long minTimeForProvider = getMinTimeLocked(provider);
957                impl.setMinTime(minTimeForProvider);
958                impl.enableLocationTracking(true);
959                updateWakelockStatusLocked();
960            } else {
961                try {
962                    // Notify the listener that updates are currently disabled
963                    if (receiver.isListener()) {
964                        receiver.getListener().onProviderDisabled(provider);
965                    }
966                } catch(RemoteException e) {
967                    Log.w(TAG, "RemoteException calling onProviderDisabled on " +
968                            receiver.getListener());
969                }
970            }
971        } finally {
972            Binder.restoreCallingIdentity(identity);
973        }
974    }
975
976    public void removeUpdates(ILocationListener listener) {
977        try {
978            synchronized (mLock) {
979                removeUpdatesLocked(getReceiver(listener));
980            }
981        } catch (SecurityException se) {
982            throw se;
983        } catch (Exception e) {
984            Log.e(TAG, "removeUpdates got exception:", e);
985        }
986    }
987
988    public void removeUpdatesPI(PendingIntent intent) {
989        try {
990            synchronized (mLock) {
991                removeUpdatesLocked(getReceiver(intent));
992            }
993        } catch (SecurityException se) {
994            throw se;
995        } catch (Exception e) {
996            Log.e(TAG, "removeUpdates got exception:", e);
997        }
998    }
999
1000    private void removeUpdatesLocked(Receiver receiver) {
1001        if (LOCAL_LOGV) {
1002            Log.v(TAG, "_removeUpdates: listener = " + receiver);
1003        }
1004
1005        // so wakelock calls will succeed
1006        final int callingUid = Binder.getCallingUid();
1007        long identity = Binder.clearCallingIdentity();
1008        try {
1009            if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1010                receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1011            }
1012
1013            // Record which providers were associated with this listener
1014            HashSet<String> providers = new HashSet<String>();
1015            HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
1016            if (oldRecords != null) {
1017                // Call dispose() on the obsolete update records.
1018                for (UpdateRecord record : oldRecords.values()) {
1019                    if (!providerHasListener(record.mProvider, callingUid, receiver)) {
1020                        LocationProviderImpl impl =
1021                                LocationProviderImpl.getProvider(record.mProvider);
1022                        if (impl != null) {
1023                            impl.removeListener(callingUid);
1024                        }
1025                    }
1026                    record.disposeLocked();
1027                }
1028                // Accumulate providers
1029                providers.addAll(oldRecords.keySet());
1030            }
1031
1032            // See if the providers associated with this listener have any
1033            // other listeners; if one does, inform it of the new smallest minTime
1034            // value; if one does not, disable location tracking for it
1035            for (String provider : providers) {
1036                // If provider is already disabled, don't need to do anything
1037                if (!isAllowedBySettingsLocked(provider)) {
1038                    continue;
1039                }
1040
1041                boolean hasOtherListener = false;
1042                ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1043                if (recordsForProvider != null && recordsForProvider.size() > 0) {
1044                    hasOtherListener = true;
1045                }
1046
1047                LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1048                if (p != null) {
1049                    if (hasOtherListener) {
1050                        p.setMinTime(getMinTimeLocked(provider));
1051                    } else {
1052                        p.enableLocationTracking(false);
1053                    }
1054                }
1055            }
1056
1057            updateWakelockStatusLocked();
1058        } finally {
1059            Binder.restoreCallingIdentity(identity);
1060        }
1061    }
1062
1063    public boolean addGpsStatusListener(IGpsStatusListener listener) {
1064        if (mGpsLocationProvider == null) {
1065            return false;
1066        }
1067        if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1068                PackageManager.PERMISSION_GRANTED) {
1069            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1070        }
1071
1072        try {
1073            mGpsLocationProvider.addGpsStatusListener(listener);
1074        } catch (RemoteException e) {
1075            Log.w(TAG, "RemoteException in addGpsStatusListener");
1076            return false;
1077        }
1078        return true;
1079    }
1080
1081    public void removeGpsStatusListener(IGpsStatusListener listener) {
1082        synchronized (mLock) {
1083            mGpsLocationProvider.removeGpsStatusListener(listener);
1084        }
1085    }
1086
1087    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1088        // first check for permission to the provider
1089        checkPermissionsSafe(provider);
1090        // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1091        if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1092                != PackageManager.PERMISSION_GRANTED)) {
1093            throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1094        }
1095
1096        synchronized (mLock) {
1097            LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1098            if (provider == null) {
1099                return false;
1100            }
1101
1102            return impl.sendExtraCommand(command, extras);
1103        }
1104    }
1105
1106    class ProximityAlert {
1107        final int  mUid;
1108        final double mLatitude;
1109        final double mLongitude;
1110        final float mRadius;
1111        final long mExpiration;
1112        final PendingIntent mIntent;
1113        final Location mLocation;
1114
1115        public ProximityAlert(int uid, double latitude, double longitude,
1116            float radius, long expiration, PendingIntent intent) {
1117            mUid = uid;
1118            mLatitude = latitude;
1119            mLongitude = longitude;
1120            mRadius = radius;
1121            mExpiration = expiration;
1122            mIntent = intent;
1123
1124            mLocation = new Location("");
1125            mLocation.setLatitude(latitude);
1126            mLocation.setLongitude(longitude);
1127        }
1128
1129        long getExpiration() {
1130            return mExpiration;
1131        }
1132
1133        PendingIntent getIntent() {
1134            return mIntent;
1135        }
1136
1137        boolean isInProximity(double latitude, double longitude) {
1138            Location loc = new Location("");
1139            loc.setLatitude(latitude);
1140            loc.setLongitude(longitude);
1141
1142            double radius = loc.distanceTo(mLocation);
1143            return radius <= mRadius;
1144        }
1145
1146        @Override
1147        public String toString() {
1148            return "ProximityAlert{"
1149                    + Integer.toHexString(System.identityHashCode(this))
1150                    + " uid " + mUid + mIntent + "}";
1151        }
1152
1153        void dump(PrintWriter pw, String prefix) {
1154            pw.println(prefix + this);
1155            pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1156            pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1157            pw.println(prefix + "mIntent=" + mIntent);
1158            pw.println(prefix + "mLocation:");
1159            mLocation.dump(new PrintWriterPrinter(pw), prefix + "  ");
1160        }
1161    }
1162
1163    // Listener for receiving locations to trigger proximity alerts
1164    class ProximityListener extends ILocationListener.Stub {
1165
1166        boolean isGpsAvailable = false;
1167
1168        // Note: this is called with the lock held.
1169        public void onLocationChanged(Location loc) {
1170
1171            // If Gps is available, then ignore updates from NetworkLocationProvider
1172            if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1173                isGpsAvailable = true;
1174            }
1175            if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1176                return;
1177            }
1178
1179            // Process proximity alerts
1180            long now = System.currentTimeMillis();
1181            double latitude = loc.getLatitude();
1182            double longitude = loc.getLongitude();
1183            ArrayList<PendingIntent> intentsToRemove = null;
1184
1185            for (ProximityAlert alert : mProximityAlerts.values()) {
1186                PendingIntent intent = alert.getIntent();
1187                long expiration = alert.getExpiration();
1188
1189                if ((expiration == -1) || (now <= expiration)) {
1190                    boolean entered = mProximitiesEntered.contains(alert);
1191                    boolean inProximity =
1192                        alert.isInProximity(latitude, longitude);
1193                    if (!entered && inProximity) {
1194                        if (LOCAL_LOGV) {
1195                            Log.v(TAG, "Entered alert");
1196                        }
1197                        mProximitiesEntered.add(alert);
1198                        Intent enteredIntent = new Intent();
1199                        enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1200                        try {
1201                            intent.send(mContext, 0, enteredIntent, null, null);
1202                        } catch (PendingIntent.CanceledException e) {
1203                            if (LOCAL_LOGV) {
1204                                Log.v(TAG, "Canceled proximity alert: " + alert, e);
1205                            }
1206                            if (intentsToRemove == null) {
1207                                intentsToRemove = new ArrayList<PendingIntent>();
1208                            }
1209                            intentsToRemove.add(intent);
1210                        }
1211                    } else if (entered && !inProximity) {
1212                        if (LOCAL_LOGV) {
1213                            Log.v(TAG, "Exited alert");
1214                        }
1215                        mProximitiesEntered.remove(alert);
1216                        Intent exitedIntent = new Intent();
1217                        exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1218                        try {
1219                            intent.send(mContext, 0, exitedIntent, null, null);
1220                        } catch (PendingIntent.CanceledException e) {
1221                            if (LOCAL_LOGV) {
1222                                Log.v(TAG, "Canceled proximity alert: " + alert, e);
1223                            }
1224                            if (intentsToRemove == null) {
1225                                intentsToRemove = new ArrayList<PendingIntent>();
1226                            }
1227                            intentsToRemove.add(intent);
1228                        }
1229                    }
1230                } else {
1231                    // Mark alert for expiration
1232                    if (LOCAL_LOGV) {
1233                        Log.v(TAG, "Expiring proximity alert: " + alert);
1234                    }
1235                    if (intentsToRemove == null) {
1236                        intentsToRemove = new ArrayList<PendingIntent>();
1237                    }
1238                    intentsToRemove.add(alert.getIntent());
1239                }
1240            }
1241
1242            // Remove expired alerts
1243            if (intentsToRemove != null) {
1244                for (PendingIntent i : intentsToRemove) {
1245                    mProximityAlerts.remove(i);
1246                    ProximityAlert alert = mProximityAlerts.get(i);
1247                    mProximitiesEntered.remove(alert);
1248                }
1249            }
1250
1251        }
1252
1253        // Note: this is called with the lock held.
1254        public void onProviderDisabled(String provider) {
1255            if (provider.equals(LocationManager.GPS_PROVIDER)) {
1256                isGpsAvailable = false;
1257            }
1258        }
1259
1260        // Note: this is called with the lock held.
1261        public void onProviderEnabled(String provider) {
1262            // ignore
1263        }
1264
1265        // Note: this is called with the lock held.
1266        public void onStatusChanged(String provider, int status, Bundle extras) {
1267            if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1268                (status != LocationProvider.AVAILABLE)) {
1269                isGpsAvailable = false;
1270            }
1271        }
1272    }
1273
1274    public void addProximityAlert(double latitude, double longitude,
1275        float radius, long expiration, PendingIntent intent) {
1276        try {
1277            synchronized (mLock) {
1278                addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1279            }
1280        } catch (SecurityException se) {
1281            throw se;
1282        } catch (Exception e) {
1283            Log.e(TAG, "addProximityAlert got exception:", e);
1284        }
1285    }
1286
1287    private void addProximityAlertLocked(double latitude, double longitude,
1288        float radius, long expiration, PendingIntent intent) {
1289        if (LOCAL_LOGV) {
1290            Log.v(TAG, "addProximityAlert: latitude = " + latitude +
1291                    ", longitude = " + longitude +
1292                    ", expiration = " + expiration +
1293                    ", intent = " + intent);
1294        }
1295
1296        // Require ability to access all providers for now
1297        if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1298            !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1299            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1300        }
1301
1302        if (expiration != -1) {
1303            expiration += System.currentTimeMillis();
1304        }
1305        ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1306                latitude, longitude, radius, expiration, intent);
1307        mProximityAlerts.put(intent, alert);
1308
1309        if (mProximityListener == null) {
1310            mProximityListener = new Receiver(new ProximityListener());
1311
1312            LocationProvider provider = LocationProviderImpl.getProvider(
1313                LocationManager.GPS_PROVIDER);
1314            if (provider != null) {
1315                requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1316            }
1317
1318            provider =
1319                LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1320            if (provider != null) {
1321                requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1322            }
1323        }
1324    }
1325
1326    public void removeProximityAlert(PendingIntent intent) {
1327        try {
1328            synchronized (mLock) {
1329               removeProximityAlertLocked(intent);
1330            }
1331        } catch (SecurityException se) {
1332            throw se;
1333        } catch (Exception e) {
1334            Log.e(TAG, "removeProximityAlert got exception:", e);
1335        }
1336    }
1337
1338    private void removeProximityAlertLocked(PendingIntent intent) {
1339        if (LOCAL_LOGV) {
1340            Log.v(TAG, "removeProximityAlert: intent = " + intent);
1341        }
1342
1343        mProximityAlerts.remove(intent);
1344        if (mProximityAlerts.size() == 0) {
1345            removeUpdatesLocked(mProximityListener);
1346            mProximityListener = null;
1347        }
1348     }
1349
1350    /**
1351     * @return null if the provider does not exits
1352     * @throw SecurityException if the provider is not allowed to be
1353     * accessed by the caller
1354     */
1355    public Bundle getProviderInfo(String provider) {
1356        try {
1357            synchronized (mLock) {
1358                return _getProviderInfoLocked(provider);
1359            }
1360        } catch (SecurityException se) {
1361            throw se;
1362        } catch (Exception e) {
1363            Log.e(TAG, "_getProviderInfo got exception:", e);
1364            return null;
1365        }
1366    }
1367
1368    private Bundle _getProviderInfoLocked(String provider) {
1369        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1370        if (p == null) {
1371            return null;
1372        }
1373
1374        checkPermissionsSafe(provider);
1375
1376        Bundle b = new Bundle();
1377        b.putBoolean("network", p.requiresNetwork());
1378        b.putBoolean("satellite", p.requiresSatellite());
1379        b.putBoolean("cell", p.requiresCell());
1380        b.putBoolean("cost", p.hasMonetaryCost());
1381        b.putBoolean("altitude", p.supportsAltitude());
1382        b.putBoolean("speed", p.supportsSpeed());
1383        b.putBoolean("bearing", p.supportsBearing());
1384        b.putInt("power", p.getPowerRequirement());
1385        b.putInt("accuracy", p.getAccuracy());
1386
1387        return b;
1388    }
1389
1390    public boolean isProviderEnabled(String provider) {
1391        try {
1392            synchronized (mLock) {
1393                return _isProviderEnabledLocked(provider);
1394            }
1395        } catch (SecurityException se) {
1396            throw se;
1397        } catch (Exception e) {
1398            Log.e(TAG, "isProviderEnabled got exception:", e);
1399            return false;
1400        }
1401    }
1402
1403    public void setLocation(Location location) {
1404        mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1405        Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1406        mLocationHandler.sendMessageAtFrontOfQueue(m);
1407    }
1408
1409    private boolean _isProviderEnabledLocked(String provider) {
1410        checkPermissionsSafe(provider);
1411
1412        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1413        if (p == null) {
1414            throw new IllegalArgumentException("provider=" + provider);
1415        }
1416        return isAllowedBySettingsLocked(provider);
1417    }
1418
1419    public Location getLastKnownLocation(String provider) {
1420        try {
1421            synchronized (mLock) {
1422                return _getLastKnownLocationLocked(provider);
1423            }
1424        } catch (SecurityException se) {
1425            throw se;
1426        } catch (Exception e) {
1427            Log.e(TAG, "getLastKnownLocation got exception:", e);
1428            return null;
1429        }
1430    }
1431
1432    private Location _getLastKnownLocationLocked(String provider) {
1433        checkPermissionsSafe(provider);
1434
1435        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1436        if (p == null) {
1437            throw new IllegalArgumentException("provider=" + provider);
1438        }
1439
1440        if (!isAllowedBySettingsLocked(provider)) {
1441            return null;
1442        }
1443
1444        Location location = mLastKnownLocation.get(provider);
1445        if (location == null) {
1446            // Get the persistent last known location for the provider
1447            location = readLastKnownLocationLocked(provider);
1448            if (location != null) {
1449                mLastKnownLocation.put(provider, location);
1450            }
1451        }
1452
1453        return location;
1454    }
1455
1456    private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1457        // Always broadcast the first update
1458        if (lastLoc == null) {
1459            return true;
1460        }
1461
1462        // Don't broadcast same location again regardless of condition
1463        // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1464        if (loc.getTime() == lastLoc.getTime()) {
1465            return false;
1466        }
1467
1468        // Check whether sufficient distance has been traveled
1469        double minDistance = record.mMinDistance;
1470        if (minDistance > 0.0) {
1471            if (loc.distanceTo(lastLoc) <= minDistance) {
1472                return false;
1473            }
1474        }
1475
1476        return true;
1477    }
1478
1479    private void handleLocationChangedLocked(Location location) {
1480        String provider = location.getProvider();
1481        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1482        if (records == null || records.size() == 0) {
1483            return;
1484        }
1485
1486        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1487        if (p == null) {
1488            return;
1489        }
1490
1491        // Update last known location for provider
1492        Location lastLocation = mLastKnownLocation.get(provider);
1493        if (lastLocation == null) {
1494            mLastKnownLocation.put(provider, new Location(location));
1495        } else {
1496            lastLocation.set(location);
1497        }
1498        writeLastKnownLocationLocked(provider, location);
1499
1500        if (LocationManager.NETWORK_PROVIDER.equals(p.getName())) {
1501            mWakeLockNetworkReceived = true;
1502        } else if (p instanceof GpsLocationProvider) {
1503            // Gps location received signal is in NetworkStateBroadcastReceiver
1504        }
1505
1506        // Fetch latest status update time
1507        long newStatusUpdateTime = p.getStatusUpdateTime();
1508
1509       // Get latest status
1510        Bundle extras = new Bundle();
1511        int status = p.getStatus(extras);
1512
1513        ArrayList<Receiver> deadReceivers = null;
1514
1515        // Broadcast location or status to all listeners
1516        final int N = records.size();
1517        for (int i=0; i<N; i++) {
1518            UpdateRecord r = records.get(i);
1519            Receiver receiver = r.mReceiver;
1520
1521            Location lastLoc = r.mLastFixBroadcast;
1522            if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1523                if (lastLoc == null) {
1524                    lastLoc = new Location(location);
1525                    r.mLastFixBroadcast = lastLoc;
1526                } else {
1527                    lastLoc.set(location);
1528                }
1529                if (!receiver.callLocationChangedLocked(location)) {
1530                    Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1531                    if (deadReceivers == null) {
1532                        deadReceivers = new ArrayList<Receiver>();
1533                    }
1534                    deadReceivers.add(receiver);
1535                }
1536            }
1537
1538            long prevStatusUpdateTime = r.mLastStatusBroadcast;
1539            if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1540                (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1541
1542                r.mLastStatusBroadcast = newStatusUpdateTime;
1543                if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1544                    Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1545                    if (deadReceivers == null) {
1546                        deadReceivers = new ArrayList<Receiver>();
1547                    }
1548                    if (!deadReceivers.contains(receiver)) {
1549                        deadReceivers.add(receiver);
1550                    }
1551                }
1552            }
1553        }
1554
1555        if (deadReceivers != null) {
1556            for (int i=deadReceivers.size()-1; i>=0; i--) {
1557                removeUpdatesLocked(deadReceivers.get(i));
1558            }
1559        }
1560    }
1561
1562    private class LocationWorkerHandler extends Handler {
1563
1564        @Override
1565        public void handleMessage(Message msg) {
1566            try {
1567                if (msg.what == MESSAGE_LOCATION_CHANGED) {
1568                    // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
1569
1570                    synchronized (mLock) {
1571                        Location location = (Location) msg.obj;
1572
1573                        if (mCollector != null &&
1574                                LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
1575                            try {
1576                                mCollector.updateLocation(location);
1577                            } catch (RemoteException e) {
1578                                Log.w(TAG, "mCollector.updateLocation failed");
1579                            }
1580                        }
1581
1582                        String provider = location.getProvider();
1583                        if (!isAllowedBySettingsLocked(provider)) {
1584                            return;
1585                        }
1586
1587                        handleLocationChangedLocked(location);
1588
1589                        if ((mWakeLockAcquireTime != 0) &&
1590                            (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1591                                > MAX_TIME_FOR_WAKE_LOCK)) {
1592
1593                            removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1594
1595                            log("LocationWorkerHandler: Exceeded max time for wake lock");
1596                            Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1597                            sendMessageAtFrontOfQueue(m);
1598
1599                        } else if (mWakeLockAcquireTime != 0 &&
1600                            mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1601
1602                            removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1603
1604                            log("LocationWorkerHandler: Locations received.");
1605                            mWakeLockAcquireTime = 0;
1606                            Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1607                            sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1608                        }
1609                    }
1610                } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1611                    log("LocationWorkerHandler: Release");
1612
1613                    // Update wakelock status so the next alarm is set before releasing wakelock
1614                    synchronized (mLock) {
1615                        updateWakelockStatusLocked();
1616                        releaseWakeLockLocked();
1617                    }
1618                }
1619            } catch (Exception e) {
1620                // Log, don't crash!
1621                Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1622            }
1623        }
1624    }
1625
1626    private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1627        @Override public void onReceive(Context context, Intent intent) {
1628            String action = intent.getAction();
1629
1630            if (action.equals(ALARM_INTENT)) {
1631                synchronized (mLock) {
1632                    log("PowerStateBroadcastReceiver: Alarm received");
1633                    // Have to do this immediately, rather than posting a
1634                    // message, so we execute our code while the system
1635                    // is holding a wake lock until the alarm broadcast
1636                    // is finished.
1637                    acquireWakeLockLocked();
1638                }
1639            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1640                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
1641                synchronized (mLock) {
1642                    int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1643                    if (uid >= 0) {
1644                        ArrayList<Receiver> removedRecs = null;
1645                        for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1646                            for (int j=i.size()-1; j>=0; j--) {
1647                                UpdateRecord ur = i.get(j);
1648                                if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1649                                    if (removedRecs == null) {
1650                                        removedRecs = new ArrayList<Receiver>();
1651                                    }
1652                                    if (!removedRecs.contains(ur.mReceiver)) {
1653                                        removedRecs.add(ur.mReceiver);
1654                                    }
1655                                }
1656                            }
1657                        }
1658                        ArrayList<ProximityAlert> removedAlerts = null;
1659                        for (ProximityAlert i : mProximityAlerts.values()) {
1660                            if (i.mUid == uid) {
1661                                if (removedAlerts == null) {
1662                                    removedAlerts = new ArrayList<ProximityAlert>();
1663                                }
1664                                if (!removedAlerts.contains(i)) {
1665                                    removedAlerts.add(i);
1666                                }
1667                            }
1668                        }
1669                        if (removedRecs != null) {
1670                            for (int i=removedRecs.size()-1; i>=0; i--) {
1671                                removeUpdatesLocked(removedRecs.get(i));
1672                            }
1673                        }
1674                        if (removedAlerts != null) {
1675                            for (int i=removedAlerts.size()-1; i>=0; i--) {
1676                                removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1677                            }
1678                        }
1679                    }
1680                }
1681            }
1682        }
1683    }
1684
1685    private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1686        @Override public void onReceive(Context context, Intent intent) {
1687            String action = intent.getAction();
1688
1689            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1690                boolean noConnectivity =
1691                    intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1692                if (!noConnectivity) {
1693                    mNetworkState = LocationProvider.AVAILABLE;
1694                } else {
1695                    mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
1696                }
1697
1698                // Notify location providers of current network state
1699                synchronized (mLock) {
1700                    List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1701                    for (LocationProviderImpl provider : providers) {
1702                        if (provider.requiresNetwork()) {
1703                            provider.updateNetworkState(mNetworkState);
1704                        }
1705                    }
1706                }
1707            } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1708
1709                final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1710                    false);
1711
1712                synchronized (mLock) {
1713                    if (!enabled) {
1714                        // When GPS is disabled, we are OK to release wake-lock
1715                        mWakeLockGpsReceived = true;
1716                    }
1717                }
1718            }
1719
1720        }
1721    }
1722
1723    // Wake locks
1724
1725    private void updateWakelockStatusLocked() {
1726        log("updateWakelockStatus()");
1727
1728        long callerId = Binder.clearCallingIdentity();
1729
1730        boolean needsLock = false;
1731        long minTime = Integer.MAX_VALUE;
1732
1733        if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1734            needsLock = true;
1735            minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1736        }
1737
1738        if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1739            needsLock = true;
1740            minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1741        }
1742
1743        PendingIntent sender =
1744            PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1745
1746        // Cancel existing alarm
1747        log("Cancelling existing alarm");
1748        mAlarmManager.cancel(sender);
1749
1750        if (needsLock) {
1751            long now = SystemClock.elapsedRealtime();
1752            mAlarmManager.set(
1753                AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1754            mAlarmInterval = minTime;
1755            log("Creating a new wakelock alarm with minTime = " + minTime);
1756        } else {
1757            log("No need for alarm");
1758            mAlarmInterval = -1;
1759
1760            // Clear out existing wakelocks
1761            mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1762            releaseWakeLockLocked();
1763        }
1764        Binder.restoreCallingIdentity(callerId);
1765    }
1766
1767    private void acquireWakeLockLocked() {
1768        try {
1769            acquireWakeLockXLocked();
1770        } catch (Exception e) {
1771            // This is to catch a runtime exception thrown when we try to release an
1772            // already released lock.
1773            Log.e(TAG, "exception in acquireWakeLock()", e);
1774        }
1775    }
1776
1777    private void acquireWakeLockXLocked() {
1778        if (mWakeLock.isHeld()) {
1779            log("Must release wakelock before acquiring");
1780            mWakeLockAcquireTime = 0;
1781            mWakeLock.release();
1782        }
1783
1784        boolean networkActive = (mNetworkLocationProvider != null)
1785                && mNetworkLocationProvider.isLocationTracking();
1786        boolean gpsActive = (mGpsLocationProvider != null)
1787                && mGpsLocationProvider.isLocationTracking();
1788
1789        boolean needsLock = networkActive || gpsActive;
1790        if (!needsLock) {
1791            log("No need for Lock!");
1792            return;
1793        }
1794
1795        mWakeLockGpsReceived = !gpsActive;
1796        mWakeLockNetworkReceived = !networkActive;
1797
1798        // Acquire wake lock
1799        mWakeLock.acquire();
1800        mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1801        log("Acquired wakelock");
1802
1803        if (mNetworkLocationProvider != null) {
1804            mNetworkLocationProvider.wakeLockAcquired();
1805        }
1806        if (mGpsLocationProvider != null) {
1807            mGpsLocationProvider.wakeLockAcquired();
1808        }
1809    }
1810
1811    private void releaseWakeLockLocked() {
1812        try {
1813            releaseWakeLockXLocked();
1814        } catch (Exception e) {
1815            // This is to catch a runtime exception thrown when we try to release an
1816            // already released lock.
1817            Log.e(TAG, "exception in releaseWakeLock()", e);
1818        }
1819    }
1820
1821    private void releaseWakeLockXLocked() {
1822        if (mNetworkLocationProvider != null) {
1823            mNetworkLocationProvider.wakeLockReleased();
1824        }
1825        if (mGpsLocationProvider != null) {
1826            mGpsLocationProvider.wakeLockReleased();
1827        }
1828
1829        // Release wake lock
1830        mWakeLockAcquireTime = 0;
1831        if (mWakeLock.isHeld()) {
1832            log("Released wakelock");
1833            mWakeLock.release();
1834        } else {
1835            log("Can't release wakelock again!");
1836        }
1837    }
1838
1839    // Geocoder
1840
1841    public String getFromLocation(double latitude, double longitude, int maxResults,
1842            String language, String country, String variant, String appName, List<Address> addrs) {
1843        if (mGeocodeProvider != null) {
1844            try {
1845                return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1846                        variant, appName,  addrs);
1847            } catch (RemoteException e) {
1848                Log.e(TAG, "getFromLocation failed", e);
1849            }
1850        }
1851        return null;
1852    }
1853
1854
1855    public String getFromLocationName(String locationName,
1856            double lowerLeftLatitude, double lowerLeftLongitude,
1857            double upperRightLatitude, double upperRightLongitude, int maxResults,
1858            String language, String country, String variant, String appName, List<Address> addrs) {
1859
1860        if (mGeocodeProvider != null) {
1861            try {
1862                return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1863                        lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1864                        maxResults, language, country, variant, appName, addrs);
1865            } catch (RemoteException e) {
1866                Log.e(TAG, "getFromLocationName failed", e);
1867            }
1868        }
1869        return null;
1870    }
1871
1872    // Mock Providers
1873
1874    private void checkMockPermissionsSafe() {
1875        boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1876                Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1877        if (!allowMocks) {
1878            throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1879        }
1880
1881        if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1882            PackageManager.PERMISSION_GRANTED) {
1883            throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1884        }
1885    }
1886
1887    public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1888        boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1889        boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1890        checkMockPermissionsSafe();
1891
1892        synchronized (mLock) {
1893            MockProvider provider = new MockProvider(name, this,
1894                requiresNetwork, requiresSatellite,
1895                requiresCell, hasMonetaryCost, supportsAltitude,
1896                supportsSpeed, supportsBearing, powerRequirement, accuracy);
1897            if (LocationProviderImpl.getProvider(name) != null) {
1898                throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1899            }
1900            LocationProviderImpl.addProvider(provider);
1901            mMockProviders.put(name, provider);
1902            updateProvidersLocked();
1903        }
1904    }
1905
1906    public void removeTestProvider(String provider) {
1907        checkMockPermissionsSafe();
1908        synchronized (mLock) {
1909            MockProvider mockProvider = mMockProviders.get(provider);
1910            if (mockProvider == null) {
1911                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1912            }
1913            LocationProviderImpl.removeProvider(mockProvider);
1914            mMockProviders.remove(mockProvider);
1915            updateProvidersLocked();
1916        }
1917    }
1918
1919    public void setTestProviderLocation(String provider, Location loc) {
1920        checkMockPermissionsSafe();
1921        synchronized (mLock) {
1922            MockProvider mockProvider = mMockProviders.get(provider);
1923            if (mockProvider == null) {
1924                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1925            }
1926            mockProvider.setLocation(loc);
1927        }
1928    }
1929
1930    public void clearTestProviderLocation(String provider) {
1931        checkMockPermissionsSafe();
1932        synchronized (mLock) {
1933            MockProvider mockProvider = mMockProviders.get(provider);
1934            if (mockProvider == null) {
1935                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1936            }
1937            mockProvider.clearLocation();
1938        }
1939    }
1940
1941    public void setTestProviderEnabled(String provider, boolean enabled) {
1942        checkMockPermissionsSafe();
1943        synchronized (mLock) {
1944            MockProvider mockProvider = mMockProviders.get(provider);
1945            if (mockProvider == null) {
1946                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1947            }
1948            if (enabled) {
1949                mockProvider.enable();
1950                mEnabledProviders.add(provider);
1951                mDisabledProviders.remove(provider);
1952            } else {
1953                mockProvider.disable();
1954                mEnabledProviders.remove(provider);
1955                mDisabledProviders.add(provider);
1956            }
1957            updateProvidersLocked();
1958        }
1959    }
1960
1961    public void clearTestProviderEnabled(String provider) {
1962        checkMockPermissionsSafe();
1963        synchronized (mLock) {
1964            MockProvider mockProvider = mMockProviders.get(provider);
1965            if (mockProvider == null) {
1966                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1967            }
1968            mEnabledProviders.remove(provider);
1969            mDisabledProviders.remove(provider);
1970            updateProvidersLocked();
1971        }
1972    }
1973
1974    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1975        checkMockPermissionsSafe();
1976        synchronized (mLock) {
1977            MockProvider mockProvider = mMockProviders.get(provider);
1978            if (mockProvider == null) {
1979                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1980            }
1981            mockProvider.setStatus(status, extras, updateTime);
1982        }
1983    }
1984
1985    public void clearTestProviderStatus(String provider) {
1986        checkMockPermissionsSafe();
1987        synchronized (mLock) {
1988            MockProvider mockProvider = mMockProviders.get(provider);
1989            if (mockProvider == null) {
1990                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1991            }
1992            mockProvider.clearStatus();
1993        }
1994    }
1995
1996    private void log(String log) {
1997        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1998            Log.d(TAG, log);
1999        }
2000    }
2001
2002    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2003        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2004                != PackageManager.PERMISSION_GRANTED) {
2005            pw.println("Permission Denial: can't dump AlarmManager from from pid="
2006                    + Binder.getCallingPid()
2007                    + ", uid=" + Binder.getCallingUid());
2008            return;
2009        }
2010
2011        synchronized (mLock) {
2012            pw.println("Current Location Manager state:");
2013            pw.println("  sProvidersLoaded=" + sProvidersLoaded);
2014            pw.println("  mGpsLocationProvider=" + mGpsLocationProvider);
2015            pw.println("  mNetworkLocationProvider=" + mNetworkLocationProvider);
2016            pw.println("  mCollector=" + mCollector);
2017            pw.println("  mAlarmInterval=" + mAlarmInterval
2018                    + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2019            pw.println("  mWakeLockGpsReceived=" + mWakeLockGpsReceived
2020                    + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
2021            pw.println("  Listeners:");
2022            int N = mReceivers.size();
2023            for (int i=0; i<N; i++) {
2024                pw.println("    " + mReceivers.get(i));
2025            }
2026            pw.println("  Location Listeners:");
2027            for (Receiver i : mReceivers.values()) {
2028                pw.println("    " + i + ":");
2029                for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
2030                    pw.println("      " + j.getKey() + ":");
2031                    j.getValue().dump(pw, "        ");
2032                }
2033            }
2034            pw.println("  Records by Provider:");
2035            for (Map.Entry<String, ArrayList<UpdateRecord>> i
2036                    : mRecordsByProvider.entrySet()) {
2037                pw.println("    " + i.getKey() + ":");
2038                for (UpdateRecord j : i.getValue()) {
2039                    pw.println("      " + j + ":");
2040                    j.dump(pw, "        ");
2041                }
2042            }
2043            pw.println("  Last Known Locations:");
2044            for (Map.Entry<String, Location> i
2045                    : mLastKnownLocation.entrySet()) {
2046                pw.println("    " + i.getKey() + ":");
2047                i.getValue().dump(new PrintWriterPrinter(pw), "      ");
2048            }
2049            if (mProximityAlerts.size() > 0) {
2050                pw.println("  Proximity Alerts:");
2051                for (Map.Entry<PendingIntent, ProximityAlert> i
2052                        : mProximityAlerts.entrySet()) {
2053                    pw.println("    " + i.getKey() + ":");
2054                    i.getValue().dump(pw, "      ");
2055                }
2056            }
2057            if (mProximitiesEntered.size() > 0) {
2058                pw.println("  Proximities Entered:");
2059                for (ProximityAlert i : mProximitiesEntered) {
2060                    pw.println("    " + i + ":");
2061                    i.dump(pw, "      ");
2062                }
2063            }
2064            pw.println("  mProximityListener=" + mProximityListener);
2065            if (mEnabledProviders.size() > 0) {
2066                pw.println("  Enabled Providers:");
2067                for (String i : mEnabledProviders) {
2068                    pw.println("    " + i);
2069                }
2070
2071            }
2072            if (mDisabledProviders.size() > 0) {
2073                pw.println("  Disabled Providers:");
2074                for (String i : mDisabledProviders) {
2075                    pw.println("    " + i);
2076                }
2077
2078            }
2079            if (mMockProviders.size() > 0) {
2080                pw.println("  Mock Providers:");
2081                for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
2082                    i.getValue().dump(pw, "      ");
2083                }
2084            }
2085        }
2086    }
2087}
2088