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