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