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