LocationManagerService.java revision 595dda6d47a4eeeffc32abbfbc55edfba588d846
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.PendingIntent;
20import android.content.ContentQueryMap;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.ApplicationInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.PackageManager.NameNotFoundException;
28import android.content.res.Resources;
29import android.database.Cursor;
30import android.location.Address;
31import android.location.Criteria;
32import android.location.GeocoderParams;
33import android.location.Geofence;
34import android.location.IGpsStatusListener;
35import android.location.IGpsStatusProvider;
36import android.location.ILocationListener;
37import android.location.ILocationManager;
38import android.location.INetInitiatedListener;
39import android.location.Location;
40import android.location.LocationManager;
41import android.location.LocationProvider;
42import android.location.LocationRequest;
43import android.net.ConnectivityManager;
44import android.os.Binder;
45import android.os.Bundle;
46import android.os.Handler;
47import android.os.IBinder;
48import android.os.Looper;
49import android.os.Message;
50import android.os.PowerManager;
51import android.os.Process;
52import android.os.RemoteException;
53import android.os.SystemClock;
54import android.os.WorkSource;
55import android.provider.Settings;
56import android.provider.Settings.NameValueTable;
57import android.util.Log;
58import android.util.Slog;
59
60import com.android.internal.content.PackageMonitor;
61import com.android.internal.location.ProviderProperties;
62import com.android.internal.location.ProviderRequest;
63import com.android.server.location.GeocoderProxy;
64import com.android.server.location.GeofenceManager;
65import com.android.server.location.GpsLocationProvider;
66import com.android.server.location.LocationBlacklist;
67import com.android.server.location.LocationFudger;
68import com.android.server.location.LocationProviderInterface;
69import com.android.server.location.LocationProviderProxy;
70import com.android.server.location.MockProvider;
71import com.android.server.location.PassiveProvider;
72
73import java.io.FileDescriptor;
74import java.io.PrintWriter;
75import java.util.ArrayList;
76import java.util.Arrays;
77import java.util.HashMap;
78import java.util.HashSet;
79import java.util.List;
80import java.util.Map;
81import java.util.Observable;
82import java.util.Observer;
83import java.util.Set;
84
85/**
86 * The service class that manages LocationProviders and issues location
87 * updates and alerts.
88 */
89public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
90    private static final String TAG = "LocationManagerService";
91    public static final boolean D = false;
92
93    private static final String WAKELOCK_KEY = TAG;
94    private static final String THREAD_NAME = TAG;
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    private static final String NETWORK_LOCATION_SERVICE_ACTION =
108            "com.android.location.service.v2.NetworkLocationProvider";
109    private static final String FUSED_LOCATION_SERVICE_ACTION =
110            "com.android.location.service.FusedLocationProvider";
111
112    private static final int MSG_LOCATION_CHANGED = 1;
113
114    // Location Providers may sometimes deliver location updates
115    // slightly faster that requested - provide grace period so
116    // we don't unnecessarily filter events that are otherwise on
117    // time
118    private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
119
120    private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
121
122    private final Context mContext;
123
124    // used internally for synchronization
125    private final Object mLock = new Object();
126
127    // --- fields below are final after init() ---
128    private LocationFudger mLocationFudger;
129    private GeofenceManager mGeofenceManager;
130    private PowerManager.WakeLock mWakeLock;
131    private PackageManager mPackageManager;
132    private GeocoderProxy mGeocodeProvider;
133    private IGpsStatusProvider mGpsStatusProvider;
134    private INetInitiatedListener mNetInitiatedListener;
135    private LocationWorkerHandler mLocationHandler;
136    private PassiveProvider mPassiveProvider;  // track passive provider for special cases
137    private LocationBlacklist mBlacklist;
138
139    // --- fields below are protected by mWakeLock ---
140    private int mPendingBroadcasts;
141
142    // --- fields below are protected by mLock ---
143    // Set of providers that are explicitly enabled
144    private final Set<String> mEnabledProviders = new HashSet<String>();
145
146    // Set of providers that are explicitly disabled
147    private final Set<String> mDisabledProviders = new HashSet<String>();
148
149    // Mock (test) providers
150    private final HashMap<String, MockProvider> mMockProviders =
151            new HashMap<String, MockProvider>();
152
153    // all receivers
154    private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
155
156    // currently installed providers (with mocks replacing real providers)
157    private final ArrayList<LocationProviderInterface> mProviders =
158            new ArrayList<LocationProviderInterface>();
159
160    // real providers, saved here when mocked out
161    private final HashMap<String, LocationProviderInterface> mRealProviders =
162            new HashMap<String, LocationProviderInterface>();
163
164    // mapping from provider name to provider
165    private final HashMap<String, LocationProviderInterface> mProvidersByName =
166            new HashMap<String, LocationProviderInterface>();
167
168    // mapping from provider name to all its UpdateRecords
169    private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
170            new HashMap<String, ArrayList<UpdateRecord>>();
171
172    // mapping from provider name to last known location
173    private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
174
175    // all providers that operate over proxy, for authorizing incoming location
176    private final ArrayList<LocationProviderProxy> mProxyProviders =
177            new ArrayList<LocationProviderProxy>();
178
179    public LocationManagerService(Context context) {
180        super();
181        mContext = context;
182
183        if (D) Log.d(TAG, "Constructed");
184
185        // most startup is deferred until systemReady()
186    }
187
188    public void systemReady() {
189        Thread thread = new Thread(null, this, THREAD_NAME);
190        thread.start();
191    }
192
193    @Override
194    public void run() {
195        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
196        Looper.prepare();
197        mLocationHandler = new LocationWorkerHandler();
198        init();
199        Looper.loop();
200    }
201
202    private void init() {
203        if (D) Log.d(TAG, "init()");
204
205        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
206        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
207        mPackageManager = mContext.getPackageManager();
208
209        synchronized (mLock) {
210            loadProvidersLocked();
211        }
212        mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
213        mBlacklist.init();
214        mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
215        mLocationFudger = new LocationFudger();
216
217        // listen for settings changes
218        ContentResolver resolver = mContext.getContentResolver();
219        Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
220                "(" + NameValueTable.NAME + "=?)",
221                new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
222        ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
223                mLocationHandler);
224        settingsCursor.close();
225        query.addObserver(this);
226        mPackageMonitor.register(mContext, Looper.myLooper(), true);
227    }
228
229    private void loadProvidersLocked() {
230        if (GpsLocationProvider.isSupported()) {
231            // Create a gps location provider
232            GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
233            mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
234            mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
235            addProviderLocked(gpsProvider);
236            mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
237        }
238
239        // create a passive location provider, which is always enabled
240        PassiveProvider passiveProvider = new PassiveProvider(this);
241        addProviderLocked(passiveProvider);
242        mEnabledProviders.add(passiveProvider.getName());
243        mPassiveProvider = passiveProvider;
244
245        /*
246        Load package name(s) containing location provider support.
247        These packages can contain services implementing location providers:
248        Geocoder Provider, Network Location Provider, and
249        Fused Location Provider. They will each be searched for
250        service components implementing these providers.
251        The location framework also has support for installation
252        of new location providers at run-time. The new package does not
253        have to be explicitly listed here, however it must have a signature
254        that matches the signature of at least one package on this list.
255        */
256        Resources resources = mContext.getResources();
257        ArrayList<String> providerPackageNames = new ArrayList<String>();
258        String[] pkgs1 = resources.getStringArray(
259                com.android.internal.R.array.config_locationProviderPackageNames);
260        String[] pkgs2 = resources.getStringArray(
261                com.android.internal.R.array.config_overlay_locationProviderPackageNames);
262        if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
263        if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
264        if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
265        if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
266
267        // bind to network provider
268        LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
269                mContext,
270                LocationManager.NETWORK_PROVIDER,
271                NETWORK_LOCATION_SERVICE_ACTION,
272                providerPackageNames, mLocationHandler);
273        if (networkProvider != null) {
274            mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
275            mProxyProviders.add(networkProvider);
276            addProviderLocked(networkProvider);
277        } else {
278            Slog.w(TAG,  "no network location provider found");
279        }
280
281        // bind to fused provider
282        LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
283                mContext,
284                LocationManager.FUSED_PROVIDER,
285                FUSED_LOCATION_SERVICE_ACTION,
286                providerPackageNames, mLocationHandler);
287        if (fusedLocationProvider != null) {
288            addProviderLocked(fusedLocationProvider);
289            mProxyProviders.add(fusedLocationProvider);
290            mEnabledProviders.add(fusedLocationProvider.getName());
291        } else {
292            Slog.e(TAG, "no fused location provider found",
293                    new IllegalStateException("Location service needs a fused location provider"));
294        }
295
296        // bind to geocoder provider
297        mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
298        if (mGeocodeProvider == null) {
299            Slog.e(TAG,  "no geocoder provider found");
300        }
301
302        updateProvidersLocked();
303    }
304
305    /**
306     * A wrapper class holding either an ILocationListener or a PendingIntent to receive
307     * location updates.
308     */
309    private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
310        final int mUid;  // uid of receiver
311        final int mPid;  // pid of receiver
312        final String mPackageName;  // package name of receiver
313        final String mPermission;  // best permission that receiver has
314
315        final ILocationListener mListener;
316        final PendingIntent mPendingIntent;
317        final Object mKey;
318
319        final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
320
321        int mPendingBroadcasts;
322
323        Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
324                String packageName) {
325            mListener = listener;
326            mPendingIntent = intent;
327            if (listener != null) {
328                mKey = listener.asBinder();
329            } else {
330                mKey = intent;
331            }
332            mPermission = checkPermission();
333            mUid = uid;
334            mPid = pid;
335            mPackageName = packageName;
336        }
337
338        @Override
339        public boolean equals(Object otherObj) {
340            if (otherObj instanceof Receiver) {
341                return mKey.equals(((Receiver)otherObj).mKey);
342            }
343            return false;
344        }
345
346        @Override
347        public int hashCode() {
348            return mKey.hashCode();
349        }
350
351        @Override
352        public String toString() {
353            StringBuilder s = new StringBuilder();
354            s.append("Reciever[");
355            s.append(Integer.toHexString(System.identityHashCode(this)));
356            if (mListener != null) {
357                s.append(" listener");
358            } else {
359                s.append(" intent");
360            }
361            for (String p : mUpdateRecords.keySet()) {
362                s.append(" ").append(mUpdateRecords.get(p).toString());
363            }
364            s.append("]");
365            return s.toString();
366        }
367
368        public boolean isListener() {
369            return mListener != null;
370        }
371
372        public boolean isPendingIntent() {
373            return mPendingIntent != null;
374        }
375
376        public ILocationListener getListener() {
377            if (mListener != null) {
378                return mListener;
379            }
380            throw new IllegalStateException("Request for non-existent listener");
381        }
382
383        public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
384            if (mListener != null) {
385                try {
386                    synchronized (this) {
387                        // synchronize to ensure incrementPendingBroadcastsLocked()
388                        // is called before decrementPendingBroadcasts()
389                        mListener.onStatusChanged(provider, status, extras);
390                        // call this after broadcasting so we do not increment
391                        // if we throw an exeption.
392                        incrementPendingBroadcastsLocked();
393                    }
394                } catch (RemoteException e) {
395                    return false;
396                }
397            } else {
398                Intent statusChanged = new Intent();
399                statusChanged.putExtras(extras);
400                statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
401                try {
402                    synchronized (this) {
403                        // synchronize to ensure incrementPendingBroadcastsLocked()
404                        // is called before decrementPendingBroadcasts()
405                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
406                                mPermission);
407                        // call this after broadcasting so we do not increment
408                        // if we throw an exeption.
409                        incrementPendingBroadcastsLocked();
410                    }
411                } catch (PendingIntent.CanceledException e) {
412                    return false;
413                }
414            }
415            return true;
416        }
417
418        public boolean callLocationChangedLocked(Location location) {
419            if (mListener != null) {
420                try {
421                    synchronized (this) {
422                        // synchronize to ensure incrementPendingBroadcastsLocked()
423                        // is called before decrementPendingBroadcasts()
424                        mListener.onLocationChanged(location);
425                        // call this after broadcasting so we do not increment
426                        // if we throw an exeption.
427                        incrementPendingBroadcastsLocked();
428                    }
429                } catch (RemoteException e) {
430                    return false;
431                }
432            } else {
433                Intent locationChanged = new Intent();
434                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
435                try {
436                    synchronized (this) {
437                        // synchronize to ensure incrementPendingBroadcastsLocked()
438                        // is called before decrementPendingBroadcasts()
439                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
440                                mPermission);
441                        // call this after broadcasting so we do not increment
442                        // if we throw an exeption.
443                        incrementPendingBroadcastsLocked();
444                    }
445                } catch (PendingIntent.CanceledException e) {
446                    return false;
447                }
448            }
449            return true;
450        }
451
452        public boolean callProviderEnabledLocked(String provider, boolean enabled) {
453            if (mListener != null) {
454                try {
455                    synchronized (this) {
456                        // synchronize to ensure incrementPendingBroadcastsLocked()
457                        // is called before decrementPendingBroadcasts()
458                        if (enabled) {
459                            mListener.onProviderEnabled(provider);
460                        } else {
461                            mListener.onProviderDisabled(provider);
462                        }
463                        // call this after broadcasting so we do not increment
464                        // if we throw an exeption.
465                        incrementPendingBroadcastsLocked();
466                    }
467                } catch (RemoteException e) {
468                    return false;
469                }
470            } else {
471                Intent providerIntent = new Intent();
472                providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
473                try {
474                    synchronized (this) {
475                        // synchronize to ensure incrementPendingBroadcastsLocked()
476                        // is called before decrementPendingBroadcasts()
477                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
478                                mPermission);
479                        // call this after broadcasting so we do not increment
480                        // if we throw an exeption.
481                        incrementPendingBroadcastsLocked();
482                    }
483                } catch (PendingIntent.CanceledException e) {
484                    return false;
485                }
486            }
487            return true;
488        }
489
490        @Override
491        public void binderDied() {
492            if (D) Log.d(TAG, "Location listener died");
493
494            synchronized (mLock) {
495                removeUpdatesLocked(this);
496            }
497            synchronized (this) {
498                if (mPendingBroadcasts > 0) {
499                    LocationManagerService.this.decrementPendingBroadcasts();
500                    mPendingBroadcasts = 0;
501                }
502            }
503        }
504
505        @Override
506        public void onSendFinished(PendingIntent pendingIntent, Intent intent,
507                int resultCode, String resultData, Bundle resultExtras) {
508            synchronized (this) {
509                decrementPendingBroadcastsLocked();
510            }
511        }
512
513        // this must be called while synchronized by caller in a synchronized block
514        // containing the sending of the broadcaset
515        private void incrementPendingBroadcastsLocked() {
516            if (mPendingBroadcasts++ == 0) {
517                LocationManagerService.this.incrementPendingBroadcasts();
518            }
519        }
520
521        private void decrementPendingBroadcastsLocked() {
522            if (--mPendingBroadcasts == 0) {
523                LocationManagerService.this.decrementPendingBroadcasts();
524            }
525        }
526    }
527
528    @Override
529    public void locationCallbackFinished(ILocationListener listener) {
530        //Do not use getReceiver here as that will add the ILocationListener to
531        //the receiver list if it is not found.  If it is not found then the
532        //LocationListener was removed when it had a pending broadcast and should
533        //not be added back.
534        IBinder binder = listener.asBinder();
535        Receiver receiver = mReceivers.get(binder);
536        if (receiver != null) {
537            synchronized (receiver) {
538                // so wakelock calls will succeed
539                long identity = Binder.clearCallingIdentity();
540                receiver.decrementPendingBroadcastsLocked();
541                Binder.restoreCallingIdentity(identity);
542           }
543        }
544    }
545
546    /** Settings Observer callback */
547    @Override
548    public void update(Observable o, Object arg) {
549        synchronized (mLock) {
550            updateProvidersLocked();
551        }
552    }
553
554    private void addProviderLocked(LocationProviderInterface provider) {
555        mProviders.add(provider);
556        mProvidersByName.put(provider.getName(), provider);
557    }
558
559    private void removeProviderLocked(LocationProviderInterface provider) {
560        provider.disable();
561        mProviders.remove(provider);
562        mProvidersByName.remove(provider.getName());
563    }
564
565
566    private boolean isAllowedBySettingsLocked(String provider) {
567        if (mEnabledProviders.contains(provider)) {
568            return true;
569        }
570        if (mDisabledProviders.contains(provider)) {
571            return false;
572        }
573        // Use system settings
574        ContentResolver resolver = mContext.getContentResolver();
575
576        return Settings.Secure.isLocationProviderEnabled(resolver, provider);
577    }
578
579    /**
580     * Throw SecurityException if caller has neither COARSE or FINE.
581     * Otherwise, return the best permission.
582     */
583    private String checkPermission() {
584        if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
585                PackageManager.PERMISSION_GRANTED) {
586            return ACCESS_FINE_LOCATION;
587        } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
588                PackageManager.PERMISSION_GRANTED) {
589            return ACCESS_COARSE_LOCATION;
590        }
591
592        throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
593                "ACCESS_FINE_LOCATION permission");
594    }
595
596    /**
597     * Returns all providers by name, including passive, but excluding
598     * fused.
599     */
600    @Override
601    public List<String> getAllProviders() {
602        checkPermission();
603
604        ArrayList<String> out;
605        synchronized (mLock) {
606            out = new ArrayList<String>(mProviders.size());
607            for (LocationProviderInterface provider : mProviders) {
608                String name = provider.getName();
609                if (LocationManager.FUSED_PROVIDER.equals(name)) {
610                    continue;
611                }
612                out.add(name);
613            }
614        }
615
616        if (D) Log.d(TAG, "getAllProviders()=" + out);
617        return out;
618    }
619
620    /**
621     * Return all providers by name, that match criteria and are optionally
622     * enabled.
623     * Can return passive provider, but never returns fused provider.
624     */
625    @Override
626    public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
627        checkPermission();
628
629        ArrayList<String> out;
630        synchronized (mLock) {
631            out = new ArrayList<String>(mProviders.size());
632            for (LocationProviderInterface provider : mProviders) {
633                String name = provider.getName();
634                if (LocationManager.FUSED_PROVIDER.equals(name)) {
635                    continue;
636                }
637                if (enabledOnly && !isAllowedBySettingsLocked(name)) {
638                    continue;
639                }
640                if (criteria != null && !LocationProvider.propertiesMeetCriteria(
641                        name, provider.getProperties(), criteria)) {
642                    continue;
643                }
644                out.add(name);
645            }
646        }
647
648        if (D) Log.d(TAG, "getProviders()=" + out);
649        return out;
650    }
651
652    /**
653     * Return the name of the best provider given a Criteria object.
654     * This method has been deprecated from the public API,
655     * and the whole LoactionProvider (including #meetsCriteria)
656     * has been deprecated as well. So this method now uses
657     * some simplified logic.
658     */
659    @Override
660    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
661        String result = null;
662        checkPermission();
663
664        List<String> providers = getProviders(criteria, enabledOnly);
665        if (providers.size() < 1) {
666            result = pickBest(providers);
667            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
668            return result;
669        }
670        providers = getProviders(null, enabledOnly);
671        if (providers.size() >= 1) {
672            result = pickBest(providers);
673            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
674            return result;
675        }
676
677        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
678        return null;
679    }
680
681    private String pickBest(List<String> providers) {
682        if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
683            return LocationManager.NETWORK_PROVIDER;
684        } else if (providers.contains(LocationManager.GPS_PROVIDER)) {
685            return LocationManager.GPS_PROVIDER;
686        } else {
687            return providers.get(0);
688        }
689    }
690
691    @Override
692    public boolean providerMeetsCriteria(String provider, Criteria criteria) {
693        checkPermission();
694
695        LocationProviderInterface p = mProvidersByName.get(provider);
696        if (p == null) {
697            throw new IllegalArgumentException("provider=" + provider);
698        }
699
700        boolean result = LocationProvider.propertiesMeetCriteria(
701                p.getName(), p.getProperties(), criteria);
702        if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
703        return result;
704    }
705
706    private void updateProvidersLocked() {
707        boolean changesMade = false;
708        for (int i = mProviders.size() - 1; i >= 0; i--) {
709            LocationProviderInterface p = mProviders.get(i);
710            boolean isEnabled = p.isEnabled();
711            String name = p.getName();
712            boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
713            if (isEnabled && !shouldBeEnabled) {
714                updateProviderListenersLocked(name, false);
715                changesMade = true;
716            } else if (!isEnabled && shouldBeEnabled) {
717                updateProviderListenersLocked(name, true);
718                changesMade = true;
719            }
720        }
721        if (changesMade) {
722            mContext.sendBroadcast(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
723        }
724    }
725
726    private void updateProviderListenersLocked(String provider, boolean enabled) {
727        int listeners = 0;
728
729        LocationProviderInterface p = mProvidersByName.get(provider);
730        if (p == null) return;
731
732        ArrayList<Receiver> deadReceivers = null;
733
734        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
735        if (records != null) {
736            final int N = records.size();
737            for (int i = 0; i < N; i++) {
738                UpdateRecord record = records.get(i);
739                // Sends a notification message to the receiver
740                if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
741                    if (deadReceivers == null) {
742                        deadReceivers = new ArrayList<Receiver>();
743                    }
744                    deadReceivers.add(record.mReceiver);
745                }
746                listeners++;
747            }
748        }
749
750        if (deadReceivers != null) {
751            for (int i = deadReceivers.size() - 1; i >= 0; i--) {
752                removeUpdatesLocked(deadReceivers.get(i));
753            }
754        }
755
756        if (enabled) {
757            p.enable();
758            if (listeners > 0) {
759                applyRequirementsLocked(provider);
760            }
761        } else {
762            p.disable();
763        }
764    }
765
766    private void applyRequirementsLocked(String provider) {
767        LocationProviderInterface p = mProvidersByName.get(provider);
768        if (p == null) return;
769
770        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
771        WorkSource worksource = new WorkSource();
772        ProviderRequest providerRequest = new ProviderRequest();
773
774        if (records != null) {
775            for (UpdateRecord record : records) {
776                LocationRequest locationRequest = record.mRequest;
777
778                providerRequest.locationRequests.add(locationRequest);
779                if (locationRequest.getInterval() < providerRequest.interval) {
780                    providerRequest.reportLocation = true;
781                    providerRequest.interval = locationRequest.getInterval();
782                }
783            }
784
785            if (providerRequest.reportLocation) {
786                // calculate who to blame for power
787                // This is somewhat arbitrary. We pick a threshold interval
788                // that is slightly higher that the minimum interval, and
789                // spread the blame across all applications with a request
790                // under that threshold.
791                long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
792                for (UpdateRecord record : records) {
793                    LocationRequest locationRequest = record.mRequest;
794                    if (locationRequest.getInterval() <= thresholdInterval) {
795                        worksource.add(record.mReceiver.mUid);
796                    }
797                }
798            }
799        }
800
801        if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
802        p.setRequest(providerRequest, worksource);
803    }
804
805    private class UpdateRecord {
806        final String mProvider;
807        final LocationRequest mRequest;
808        final Receiver mReceiver;
809        Location mLastFixBroadcast;
810        long mLastStatusBroadcast;
811
812        /**
813         * Note: must be constructed with lock held.
814         */
815        UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
816            mProvider = provider;
817            mRequest = request;
818            mReceiver = receiver;
819
820            ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
821            if (records == null) {
822                records = new ArrayList<UpdateRecord>();
823                mRecordsByProvider.put(provider, records);
824            }
825            if (!records.contains(this)) {
826                records.add(this);
827            }
828        }
829
830        /**
831         * Method to be called when a record will no longer be used.  Calling this multiple times
832         * must have the same effect as calling it once.
833         */
834        void disposeLocked(boolean removeReceiver) {
835            // remove from mRecordsByProvider
836            ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
837            if (globalRecords != null) {
838                globalRecords.remove(this);
839            }
840
841            if (!removeReceiver) return;  // the caller will handle the rest
842
843            // remove from Receiver#mUpdateRecords
844            HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
845            if (receiverRecords != null) {
846                receiverRecords.remove(this.mProvider);
847
848                // and also remove the Receiver if it has no more update records
849                if (removeReceiver && receiverRecords.size() == 0) {
850                    removeUpdatesLocked(mReceiver);
851                }
852            }
853        }
854
855        @Override
856        public String toString() {
857            StringBuilder s = new StringBuilder();
858            s.append("UpdateRecord[");
859            s.append(mProvider);
860            s.append(' ').append(mReceiver.mPackageName).append('(');
861            s.append(mReceiver.mUid).append(')');
862            s.append(' ').append(mRequest);
863            s.append(']');
864            return s.toString();
865        }
866    }
867
868    private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
869        IBinder binder = listener.asBinder();
870        Receiver receiver = mReceivers.get(binder);
871        if (receiver == null) {
872            receiver = new Receiver(listener, null, pid, uid, packageName);
873            mReceivers.put(binder, receiver);
874
875            try {
876                receiver.getListener().asBinder().linkToDeath(receiver, 0);
877            } catch (RemoteException e) {
878                Slog.e(TAG, "linkToDeath failed:", e);
879                return null;
880            }
881        }
882        return receiver;
883    }
884
885    private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
886        Receiver receiver = mReceivers.get(intent);
887        if (receiver == null) {
888            receiver = new Receiver(null, intent, pid, uid, packageName);
889            mReceivers.put(intent, receiver);
890        }
891        return receiver;
892    }
893
894    private String checkPermissionAndRequest(LocationRequest request) {
895        String perm = checkPermission();
896
897        if (ACCESS_COARSE_LOCATION.equals(perm)) {
898             switch (request.getQuality()) {
899                 case LocationRequest.ACCURACY_FINE:
900                     request.setQuality(LocationRequest.ACCURACY_BLOCK);
901                     break;
902                 case LocationRequest.POWER_HIGH:
903                     request.setQuality(LocationRequest.POWER_LOW);
904                     break;
905             }
906             // throttle
907             if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
908                 request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
909             }
910             if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
911                 request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
912             }
913        }
914        // make getFastestInterval() the minimum of interval and fastest interval
915        if (request.getFastestInterval() > request.getInterval()) {
916            request.setFastestInterval(request.getInterval());
917        }
918        return perm;
919    }
920
921    private void checkPackageName(String packageName) {
922        if (packageName == null) {
923            throw new SecurityException("invalid package name: " + packageName);
924        }
925        int uid = Binder.getCallingUid();
926        String[] packages = mPackageManager.getPackagesForUid(uid);
927        if (packages == null) {
928            throw new SecurityException("invalid UID " + uid);
929        }
930        for (String pkg : packages) {
931            if (packageName.equals(pkg)) return;
932        }
933        throw new SecurityException("invalid package name: " + packageName);
934    }
935
936    private void checkPendingIntent(PendingIntent intent) {
937        if (intent == null) {
938            throw new IllegalArgumentException("invalid pending intent: " + intent);
939        }
940    }
941
942    private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
943            int pid, int uid, String packageName) {
944        if (intent == null && listener == null) {
945            throw new IllegalArgumentException("need eiter listener or intent");
946        } else if (intent != null && listener != null) {
947            throw new IllegalArgumentException("cannot register both listener and intent");
948        } else if (intent != null) {
949            checkPendingIntent(intent);
950            return getReceiver(intent, pid, uid, packageName);
951        } else {
952            return getReceiver(listener, pid, uid, packageName);
953        }
954    }
955
956    @Override
957    public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
958            PendingIntent intent, String packageName) {
959        if (request == null) request = DEFAULT_LOCATION_REQUEST;
960        checkPackageName(packageName);
961        checkPermissionAndRequest(request);
962
963        final int pid = Binder.getCallingPid();
964        final int uid = Binder.getCallingUid();
965        Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
966
967        // providers may use public location API's, need to clear identity
968        long identity = Binder.clearCallingIdentity();
969        try {
970            synchronized (mLock) {
971                requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
972            }
973        } finally {
974            Binder.restoreCallingIdentity(identity);
975        }
976    }
977
978    private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
979            int pid, int uid, String packageName) {
980        // Figure out the provider. Either its explicitly request (legacy use cases), or
981        // use the fused provider
982        if (request == null) request = DEFAULT_LOCATION_REQUEST;
983        String name = request.getProvider();
984        if (name == null) name = LocationManager.FUSED_PROVIDER;
985        LocationProviderInterface provider = mProvidersByName.get(name);
986        if (provider == null) {
987            throw new IllegalArgumentException("provider doesn't exisit: " + provider);
988        }
989
990        Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
991                name + " " + request + " from " + packageName + "(" + uid + ")");
992
993        UpdateRecord record = new UpdateRecord(name, request, receiver);
994        UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
995        if (oldRecord != null) {
996            oldRecord.disposeLocked(false);
997        }
998
999        boolean isProviderEnabled = isAllowedBySettingsLocked(name);
1000        if (isProviderEnabled) {
1001            applyRequirementsLocked(name);
1002        } else {
1003            // Notify the listener that updates are currently disabled
1004            receiver.callProviderEnabledLocked(name, false);
1005        }
1006    }
1007
1008    @Override
1009    public void removeUpdates(ILocationListener listener, PendingIntent intent,
1010            String packageName) {
1011        checkPackageName(packageName);
1012        checkPermission();
1013        final int pid = Binder.getCallingPid();
1014        final int uid = Binder.getCallingUid();
1015        Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
1016
1017        // providers may use public location API's, need to clear identity
1018        long identity = Binder.clearCallingIdentity();
1019        try {
1020            synchronized (mLock) {
1021                removeUpdatesLocked(receiver);
1022            }
1023        } finally {
1024            Binder.restoreCallingIdentity(identity);
1025        }
1026    }
1027
1028    private void removeUpdatesLocked(Receiver receiver) {
1029        Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
1030
1031        if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1032            receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1033            synchronized (receiver) {
1034                if (receiver.mPendingBroadcasts > 0) {
1035                    decrementPendingBroadcasts();
1036                    receiver.mPendingBroadcasts = 0;
1037                }
1038            }
1039        }
1040
1041        // Record which providers were associated with this listener
1042        HashSet<String> providers = new HashSet<String>();
1043        HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
1044        if (oldRecords != null) {
1045            // Call dispose() on the obsolete update records.
1046            for (UpdateRecord record : oldRecords.values()) {
1047                record.disposeLocked(false);
1048            }
1049            // Accumulate providers
1050            providers.addAll(oldRecords.keySet());
1051        }
1052
1053        // update provider
1054        for (String provider : providers) {
1055            // If provider is already disabled, don't need to do anything
1056            if (!isAllowedBySettingsLocked(provider)) {
1057                continue;
1058            }
1059
1060            applyRequirementsLocked(provider);
1061        }
1062    }
1063
1064    @Override
1065    public Location getLastLocation(LocationRequest request, String packageName) {
1066        if (D) Log.d(TAG, "getLastLocation: " + request);
1067        if (request == null) request = DEFAULT_LOCATION_REQUEST;
1068        String perm = checkPermissionAndRequest(request);
1069        checkPackageName(packageName);
1070
1071        if (mBlacklist.isBlacklisted(packageName)) {
1072            if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
1073                    packageName);
1074            return null;
1075        }
1076
1077        synchronized (mLock) {
1078            // Figure out the provider. Either its explicitly request (deprecated API's),
1079            // or use the fused provider
1080            String name = request.getProvider();
1081            if (name == null) name = LocationManager.FUSED_PROVIDER;
1082            LocationProviderInterface provider = mProvidersByName.get(name);
1083            if (provider == null) return null;
1084
1085            if (!isAllowedBySettingsLocked(name)) return null;
1086
1087            Location location = mLastLocation.get(name);
1088            if (ACCESS_FINE_LOCATION.equals(perm)) {
1089                return location;
1090            } else {
1091                return mLocationFudger.getOrCreate(location);
1092            }
1093        }
1094    }
1095
1096    @Override
1097    public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
1098            String packageName) {
1099        if (request == null) request = DEFAULT_LOCATION_REQUEST;
1100        checkPermissionAndRequest(request);
1101        checkPendingIntent(intent);
1102        checkPackageName(packageName);
1103
1104        if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
1105
1106        // geo-fence manager uses the public location API, need to clear identity
1107        int uid = Binder.getCallingUid();
1108        long identity = Binder.clearCallingIdentity();
1109        try {
1110            mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
1111        } finally {
1112            Binder.restoreCallingIdentity(identity);
1113        }
1114    }
1115
1116    @Override
1117    public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
1118        checkPermission();
1119        checkPendingIntent(intent);
1120        checkPackageName(packageName);
1121
1122        if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
1123
1124        // geo-fence manager uses the public location API, need to clear identity
1125        long identity = Binder.clearCallingIdentity();
1126        try {
1127            mGeofenceManager.removeFence(geofence, intent);
1128        } finally {
1129            Binder.restoreCallingIdentity(identity);
1130        }
1131    }
1132
1133
1134    @Override
1135    public boolean addGpsStatusListener(IGpsStatusListener listener) {
1136        if (mGpsStatusProvider == null) {
1137            return false;
1138        }
1139        if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
1140                PackageManager.PERMISSION_GRANTED) {
1141            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1142        }
1143
1144        try {
1145            mGpsStatusProvider.addGpsStatusListener(listener);
1146        } catch (RemoteException e) {
1147            Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
1148            return false;
1149        }
1150        return true;
1151    }
1152
1153    @Override
1154    public void removeGpsStatusListener(IGpsStatusListener listener) {
1155        synchronized (mLock) {
1156            try {
1157                mGpsStatusProvider.removeGpsStatusListener(listener);
1158            } catch (Exception e) {
1159                Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
1160            }
1161        }
1162    }
1163
1164    @Override
1165    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1166        if (provider == null) {
1167            // throw NullPointerException to remain compatible with previous implementation
1168            throw new NullPointerException();
1169        }
1170
1171        checkPermission();
1172        // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1173        if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1174                != PackageManager.PERMISSION_GRANTED)) {
1175            throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1176        }
1177
1178        synchronized (mLock) {
1179            LocationProviderInterface p = mProvidersByName.get(provider);
1180            if (p == null) return false;
1181
1182            return p.sendExtraCommand(command, extras);
1183        }
1184    }
1185
1186    @Override
1187    public boolean sendNiResponse(int notifId, int userResponse) {
1188        if (Binder.getCallingUid() != Process.myUid()) {
1189            throw new SecurityException(
1190                    "calling sendNiResponse from outside of the system is not allowed");
1191        }
1192        try {
1193            return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
1194        } catch (RemoteException e) {
1195            Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
1196            return false;
1197        }
1198    }
1199
1200    /**
1201     * @return null if the provider does not exist
1202     * @throws SecurityException if the provider is not allowed to be
1203     * accessed by the caller
1204     */
1205    @Override
1206    public ProviderProperties getProviderProperties(String provider) {
1207        checkPermission();
1208
1209        LocationProviderInterface p;
1210        synchronized (mLock) {
1211            p = mProvidersByName.get(provider);
1212        }
1213
1214        if (p == null) return null;
1215        return p.getProperties();
1216    }
1217
1218    @Override
1219    public boolean isProviderEnabled(String provider) {
1220        checkPermission();
1221        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
1222
1223        synchronized (mLock) {
1224            LocationProviderInterface p = mProvidersByName.get(provider);
1225            if (p == null) return false;
1226
1227            return isAllowedBySettingsLocked(provider);
1228        }
1229    }
1230
1231    private void checkCallerIsProvider() {
1232        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1233                == PackageManager.PERMISSION_GRANTED) {
1234            return;
1235        }
1236
1237        // Previously we only used the INSTALL_LOCATION_PROVIDER
1238        // check. But that is system or signature
1239        // protection level which is not flexible enough for
1240        // providers installed oustide the system image. So
1241        // also allow providers with a UID matching the
1242        // currently bound package name
1243
1244        int uid = Binder.getCallingUid();
1245
1246        if (mGeocodeProvider != null) {
1247            if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
1248        }
1249        for (LocationProviderProxy proxy : mProxyProviders) {
1250            if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
1251        }
1252        throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
1253                "or UID of a currently bound location provider");
1254    }
1255
1256    private boolean doesPackageHaveUid(int uid, String packageName) {
1257        if (packageName == null) {
1258            return false;
1259        }
1260        try {
1261            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
1262            if (appInfo.uid != uid) {
1263                return false;
1264            }
1265        } catch (NameNotFoundException e) {
1266            return false;
1267        }
1268        return true;
1269    }
1270
1271    @Override
1272    public void reportLocation(Location location, boolean passive) {
1273        checkCallerIsProvider();
1274
1275        if (!location.isComplete()) {
1276            Log.w(TAG, "Dropping incomplete location: " + location);
1277            return;
1278        }
1279
1280        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
1281        Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
1282        m.arg1 = (passive ? 1 : 0);
1283        mLocationHandler.sendMessageAtFrontOfQueue(m);
1284    }
1285
1286
1287    private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1288        // Always broadcast the first update
1289        if (lastLoc == null) {
1290            return true;
1291        }
1292
1293        // Check whether sufficient time has passed
1294        long minTime = record.mRequest.getFastestInterval();
1295        long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
1296        if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
1297            return false;
1298        }
1299
1300        // Check whether sufficient distance has been traveled
1301        double minDistance = record.mRequest.getSmallestDisplacement();
1302        if (minDistance > 0.0) {
1303            if (loc.distanceTo(lastLoc) <= minDistance) {
1304                return false;
1305            }
1306        }
1307
1308        return true;
1309    }
1310
1311    private void handleLocationChangedLocked(Location location, boolean passive) {
1312        if (D) Log.d(TAG, "incoming location: " + location);
1313
1314        long now = SystemClock.elapsedRealtime();
1315        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
1316        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1317        if (records == null || records.size() == 0) return;
1318
1319        LocationProviderInterface p = mProvidersByName.get(provider);
1320        if (p == null) return;
1321
1322        // Add the coarse location as an extra
1323        Location coarse = mLocationFudger.getOrCreate(location);
1324
1325        // Update last known locations
1326        Location lastLocation = mLastLocation.get(provider);
1327        if (lastLocation == null) {
1328            lastLocation = new Location(provider);
1329            mLastLocation.put(provider, lastLocation);
1330        }
1331        lastLocation.set(location);
1332
1333        // Fetch latest status update time
1334        long newStatusUpdateTime = p.getStatusUpdateTime();
1335
1336       // Get latest status
1337        Bundle extras = new Bundle();
1338        int status = p.getStatus(extras);
1339
1340        ArrayList<Receiver> deadReceivers = null;
1341        ArrayList<UpdateRecord> deadUpdateRecords = null;
1342
1343        // Broadcast location or status to all listeners
1344        for (UpdateRecord r : records) {
1345            Receiver receiver = r.mReceiver;
1346            boolean receiverDead = false;
1347
1348            if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
1349                if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
1350                        receiver.mPackageName);
1351                continue;
1352            }
1353
1354            if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
1355                location = lastLocation;  // use fine location
1356            } else {
1357                location = coarse;  // use coarse location
1358            }
1359
1360            Location lastLoc = r.mLastFixBroadcast;
1361            if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1362                if (lastLoc == null) {
1363                    lastLoc = new Location(location);
1364                    r.mLastFixBroadcast = lastLoc;
1365                } else {
1366                    lastLoc.set(location);
1367                }
1368                if (!receiver.callLocationChangedLocked(location)) {
1369                    Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1370                    receiverDead = true;
1371                }
1372            }
1373
1374            long prevStatusUpdateTime = r.mLastStatusBroadcast;
1375            if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1376                (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1377
1378                r.mLastStatusBroadcast = newStatusUpdateTime;
1379                if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1380                    receiverDead = true;
1381                    Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1382                }
1383            }
1384
1385            // track expired records
1386            if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
1387                if (deadUpdateRecords == null) {
1388                    deadUpdateRecords = new ArrayList<UpdateRecord>();
1389                }
1390                deadUpdateRecords.add(r);
1391            }
1392            // track dead receivers
1393            if (receiverDead) {
1394                if (deadReceivers == null) {
1395                    deadReceivers = new ArrayList<Receiver>();
1396                }
1397                if (!deadReceivers.contains(receiver)) {
1398                    deadReceivers.add(receiver);
1399                }
1400            }
1401        }
1402
1403        // remove dead records and receivers outside the loop
1404        if (deadReceivers != null) {
1405            for (Receiver receiver : deadReceivers) {
1406                removeUpdatesLocked(receiver);
1407            }
1408        }
1409        if (deadUpdateRecords != null) {
1410            for (UpdateRecord r : deadUpdateRecords) {
1411                r.disposeLocked(true);
1412            }
1413        }
1414    }
1415
1416    private class LocationWorkerHandler extends Handler {
1417        @Override
1418        public void handleMessage(Message msg) {
1419            switch (msg.what) {
1420                case MSG_LOCATION_CHANGED:
1421                    handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
1422                    break;
1423            }
1424        }
1425    }
1426
1427    private void handleLocationChanged(Location location, boolean passive) {
1428        String provider = location.getProvider();
1429
1430        if (!passive) {
1431            // notify passive provider of the new location
1432            mPassiveProvider.updateLocation(location);
1433        }
1434
1435        synchronized (mLock) {
1436            if (isAllowedBySettingsLocked(provider)) {
1437                handleLocationChangedLocked(location, passive);
1438            }
1439        }
1440    }
1441
1442    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
1443        @Override
1444        public void onPackageDisappeared(String packageName, int reason) {
1445            // remove all receivers associated with this package name
1446            synchronized (mLock) {
1447                ArrayList<Receiver> deadReceivers = null;
1448
1449                for (Receiver receiver : mReceivers.values()) {
1450                    if (receiver.mPackageName.equals(packageName)) {
1451                        if (deadReceivers == null) {
1452                            deadReceivers = new ArrayList<Receiver>();
1453                        }
1454                        deadReceivers.add(receiver);
1455                    }
1456                }
1457
1458                // perform removal outside of mReceivers loop
1459                if (deadReceivers != null) {
1460                    for (Receiver receiver : deadReceivers) {
1461                        removeUpdatesLocked(receiver);
1462                    }
1463                }
1464            }
1465        }
1466    };
1467
1468    // Wake locks
1469
1470    private void incrementPendingBroadcasts() {
1471        synchronized (mWakeLock) {
1472            if (mPendingBroadcasts++ == 0) {
1473                try {
1474                    mWakeLock.acquire();
1475                    log("Acquired wakelock");
1476                } catch (Exception e) {
1477                    // This is to catch a runtime exception thrown when we try to release an
1478                    // already released lock.
1479                    Slog.e(TAG, "exception in acquireWakeLock()", e);
1480                }
1481            }
1482        }
1483    }
1484
1485    private void decrementPendingBroadcasts() {
1486        synchronized (mWakeLock) {
1487            if (--mPendingBroadcasts == 0) {
1488                try {
1489                    // Release wake lock
1490                    if (mWakeLock.isHeld()) {
1491                        mWakeLock.release();
1492                        log("Released wakelock");
1493                    } else {
1494                        log("Can't release wakelock again!");
1495                    }
1496                } catch (Exception e) {
1497                    // This is to catch a runtime exception thrown when we try to release an
1498                    // already released lock.
1499                    Slog.e(TAG, "exception in releaseWakeLock()", e);
1500                }
1501            }
1502        }
1503    }
1504
1505    // Geocoder
1506
1507    @Override
1508    public boolean geocoderIsPresent() {
1509        return mGeocodeProvider != null;
1510    }
1511
1512    @Override
1513    public String getFromLocation(double latitude, double longitude, int maxResults,
1514            GeocoderParams params, List<Address> addrs) {
1515        if (mGeocodeProvider != null) {
1516            return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
1517                    params, addrs);
1518        }
1519        return null;
1520    }
1521
1522
1523    @Override
1524    public String getFromLocationName(String locationName,
1525            double lowerLeftLatitude, double lowerLeftLongitude,
1526            double upperRightLatitude, double upperRightLongitude, int maxResults,
1527            GeocoderParams params, List<Address> addrs) {
1528
1529        if (mGeocodeProvider != null) {
1530            return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1531                    lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1532                    maxResults, params, addrs);
1533        }
1534        return null;
1535    }
1536
1537    // Mock Providers
1538
1539    private void checkMockPermissionsSafe() {
1540        boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1541                Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1542        if (!allowMocks) {
1543            throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1544        }
1545
1546        if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1547            PackageManager.PERMISSION_GRANTED) {
1548            throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1549        }
1550    }
1551
1552    @Override
1553    public void addTestProvider(String name, ProviderProperties properties) {
1554        checkMockPermissionsSafe();
1555
1556        if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
1557            throw new IllegalArgumentException("Cannot mock the passive location provider");
1558        }
1559
1560        long identity = Binder.clearCallingIdentity();
1561        synchronized (mLock) {
1562            MockProvider provider = new MockProvider(name, this, properties);
1563            // remove the real provider if we are replacing GPS or network provider
1564            if (LocationManager.GPS_PROVIDER.equals(name)
1565                    || LocationManager.NETWORK_PROVIDER.equals(name)
1566                    || LocationManager.FUSED_PROVIDER.equals(name)) {
1567                LocationProviderInterface p = mProvidersByName.get(name);
1568                if (p != null) {
1569                    removeProviderLocked(p);
1570                }
1571            }
1572            if (mProvidersByName.get(name) != null) {
1573                throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1574            }
1575            addProviderLocked(provider);
1576            mMockProviders.put(name, provider);
1577            mLastLocation.put(name, null);
1578            updateProvidersLocked();
1579        }
1580        Binder.restoreCallingIdentity(identity);
1581    }
1582
1583    @Override
1584    public void removeTestProvider(String provider) {
1585        checkMockPermissionsSafe();
1586        synchronized (mLock) {
1587            MockProvider mockProvider = mMockProviders.get(provider);
1588            if (mockProvider == null) {
1589                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1590            }
1591            long identity = Binder.clearCallingIdentity();
1592            removeProviderLocked(mProvidersByName.get(provider));
1593            mMockProviders.remove(mockProvider);
1594
1595            // reinstate real provider if available
1596            LocationProviderInterface realProvider = mRealProviders.get(provider);
1597            if (realProvider != null) {
1598                addProviderLocked(realProvider);
1599            }
1600            mLastLocation.put(provider, null);
1601            updateProvidersLocked();
1602            Binder.restoreCallingIdentity(identity);
1603        }
1604    }
1605
1606    @Override
1607    public void setTestProviderLocation(String provider, Location loc) {
1608        checkMockPermissionsSafe();
1609        synchronized (mLock) {
1610            MockProvider mockProvider = mMockProviders.get(provider);
1611            if (mockProvider == null) {
1612                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1613            }
1614            // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1615            long identity = Binder.clearCallingIdentity();
1616            mockProvider.setLocation(loc);
1617            Binder.restoreCallingIdentity(identity);
1618        }
1619    }
1620
1621    @Override
1622    public void clearTestProviderLocation(String provider) {
1623        checkMockPermissionsSafe();
1624        synchronized (mLock) {
1625            MockProvider mockProvider = mMockProviders.get(provider);
1626            if (mockProvider == null) {
1627                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1628            }
1629            mockProvider.clearLocation();
1630        }
1631    }
1632
1633    @Override
1634    public void setTestProviderEnabled(String provider, boolean enabled) {
1635        checkMockPermissionsSafe();
1636        synchronized (mLock) {
1637            MockProvider mockProvider = mMockProviders.get(provider);
1638            if (mockProvider == null) {
1639                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1640            }
1641            long identity = Binder.clearCallingIdentity();
1642            if (enabled) {
1643                mockProvider.enable();
1644                mEnabledProviders.add(provider);
1645                mDisabledProviders.remove(provider);
1646            } else {
1647                mockProvider.disable();
1648                mEnabledProviders.remove(provider);
1649                mDisabledProviders.add(provider);
1650            }
1651            updateProvidersLocked();
1652            Binder.restoreCallingIdentity(identity);
1653        }
1654    }
1655
1656    @Override
1657    public void clearTestProviderEnabled(String provider) {
1658        checkMockPermissionsSafe();
1659        synchronized (mLock) {
1660            MockProvider mockProvider = mMockProviders.get(provider);
1661            if (mockProvider == null) {
1662                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1663            }
1664            long identity = Binder.clearCallingIdentity();
1665            mEnabledProviders.remove(provider);
1666            mDisabledProviders.remove(provider);
1667            updateProvidersLocked();
1668            Binder.restoreCallingIdentity(identity);
1669        }
1670    }
1671
1672    @Override
1673    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1674        checkMockPermissionsSafe();
1675        synchronized (mLock) {
1676            MockProvider mockProvider = mMockProviders.get(provider);
1677            if (mockProvider == null) {
1678                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1679            }
1680            mockProvider.setStatus(status, extras, updateTime);
1681        }
1682    }
1683
1684    @Override
1685    public void clearTestProviderStatus(String provider) {
1686        checkMockPermissionsSafe();
1687        synchronized (mLock) {
1688            MockProvider mockProvider = mMockProviders.get(provider);
1689            if (mockProvider == null) {
1690                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1691            }
1692            mockProvider.clearStatus();
1693        }
1694    }
1695
1696    private void log(String log) {
1697        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1698            Slog.d(TAG, log);
1699        }
1700    }
1701
1702    @Override
1703    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1704        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1705                != PackageManager.PERMISSION_GRANTED) {
1706            pw.println("Permission Denial: can't dump LocationManagerService from from pid="
1707                    + Binder.getCallingPid()
1708                    + ", uid=" + Binder.getCallingUid());
1709            return;
1710        }
1711
1712        synchronized (mLock) {
1713            pw.println("Current Location Manager state:");
1714            pw.println("  Location Listeners:");
1715            for (Receiver receiver : mReceivers.values()) {
1716                pw.println("    " + receiver);
1717            }
1718            pw.println("  Records by Provider:");
1719            for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
1720                pw.println("    " + entry.getKey() + ":");
1721                for (UpdateRecord record : entry.getValue()) {
1722                    pw.println("      " + record);
1723                }
1724            }
1725            pw.println("  Last Known Locations:");
1726            for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
1727                String provider = entry.getKey();
1728                Location location = entry.getValue();
1729                pw.println("    " + provider + ": " + location);
1730            }
1731
1732            mGeofenceManager.dump(pw);
1733
1734            if (mEnabledProviders.size() > 0) {
1735                pw.println("  Enabled Providers:");
1736                for (String i : mEnabledProviders) {
1737                    pw.println("    " + i);
1738                }
1739
1740            }
1741            if (mDisabledProviders.size() > 0) {
1742                pw.println("  Disabled Providers:");
1743                for (String i : mDisabledProviders) {
1744                    pw.println("    " + i);
1745                }
1746            }
1747            pw.append("  ");
1748            mBlacklist.dump(pw);
1749            if (mMockProviders.size() > 0) {
1750                pw.println("  Mock Providers:");
1751                for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
1752                    i.getValue().dump(pw, "      ");
1753                }
1754            }
1755
1756            pw.append("  fudger: ");
1757            mLocationFudger.dump(fd, pw,  args);
1758
1759            if (args.length > 0 && "short".equals(args[0])) {
1760                return;
1761            }
1762            for (LocationProviderInterface provider: mProviders) {
1763                pw.print(provider.getName() + " Internal State");
1764                if (provider instanceof LocationProviderProxy) {
1765                    LocationProviderProxy proxy = (LocationProviderProxy) provider;
1766                    pw.print(" (" + proxy.getConnectedPackageName() + ")");
1767                }
1768                pw.println(":");
1769                provider.dump(fd, pw, args);
1770            }
1771        }
1772    }
1773}
1774