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