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