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