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