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