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