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