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