LocationManagerService.java revision 3001a035439d8134a7d70d796376d1dfbff3cdcd
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import java.io.BufferedReader;
20import java.io.File;
21import java.io.FileDescriptor;
22import java.io.FileReader;
23import java.io.FileWriter;
24import java.io.IOException;
25import java.io.PrintWriter;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.List;
30import java.util.Map;
31import java.util.Set;
32import java.util.regex.Pattern;
33
34import android.app.AlarmManager;
35import android.app.PendingIntent;
36import android.content.BroadcastReceiver;
37import android.content.ContentResolver;
38import android.content.Context;
39import android.content.Intent;
40import android.content.IntentFilter;
41import android.content.pm.PackageManager;
42import android.location.Address;
43import android.location.IGpsStatusListener;
44import android.location.ILocationListener;
45import android.location.ILocationManager;
46import android.location.Location;
47import android.location.LocationManager;
48import android.location.LocationProvider;
49import android.location.LocationProviderImpl;
50import android.net.ConnectivityManager;
51import android.net.Uri;
52import android.net.wifi.ScanResult;
53import android.net.wifi.WifiManager;
54import android.os.Binder;
55import android.os.Bundle;
56import android.os.Handler;
57import android.os.IBinder;
58import android.os.Message;
59import android.os.PowerManager;
60import android.os.RemoteException;
61import android.os.SystemClock;
62import android.provider.Settings;
63import android.telephony.CellLocation;
64import android.telephony.PhoneStateListener;
65import android.telephony.TelephonyManager;
66import android.util.Config;
67import android.util.Log;
68import android.util.PrintWriterPrinter;
69import android.util.SparseIntArray;
70
71import com.android.internal.app.IBatteryStats;
72import com.android.internal.location.CellState;
73import com.android.internal.location.GpsLocationProvider;
74import com.android.internal.location.ILocationCollector;
75import com.android.internal.location.INetworkLocationManager;
76import com.android.internal.location.INetworkLocationProvider;
77import com.android.internal.location.TrackProvider;
78import com.android.server.am.BatteryStatsService;
79
80/**
81 * The service class that manages LocationProviders and issues location
82 * updates and alerts.
83 *
84 * {@hide}
85 */
86public class LocationManagerService extends ILocationManager.Stub
87        implements INetworkLocationManager {
88    private static final String TAG = "LocationManagerService";
89
90    // Minimum time interval between last known location writes, in milliseconds.
91    private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
92
93    // Max time to hold wake lock for, in milliseconds.
94    private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
95
96    // Time to wait after releasing a wake lock for clients to process location update,
97    // in milliseconds.
98    private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
99
100    // The last time a location was written, by provider name.
101    private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
102
103    private static final Pattern PATTERN_COMMA = Pattern.compile(",");
104
105    private static final String ACCESS_FINE_LOCATION =
106        android.Manifest.permission.ACCESS_FINE_LOCATION;
107    private static final String ACCESS_COARSE_LOCATION =
108        android.Manifest.permission.ACCESS_COARSE_LOCATION;
109    private static final String ACCESS_MOCK_LOCATION =
110        android.Manifest.permission.ACCESS_MOCK_LOCATION;
111    private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
112        android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
113
114    // Set of providers that are explicitly enabled
115    private final Set<String> mEnabledProviders = new HashSet<String>();
116
117    // Set of providers that are explicitly disabled
118    private final Set<String> mDisabledProviders = new HashSet<String>();
119
120    // Locations, status values, and extras for mock providers
121    HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
122    private final HashMap<String,Location> mMockProviderLocation = new HashMap<String,Location>();
123    private final HashMap<String,Integer> mMockProviderStatus = new HashMap<String,Integer>();
124    private final HashMap<String,Bundle> mMockProviderStatusExtras = new HashMap<String,Bundle>();
125    private final HashMap<String,Long> mMockProviderStatusUpdateTime = new HashMap<String,Long>();
126
127    private static boolean sProvidersLoaded = false;
128
129    private final Context mContext;
130    private GpsLocationProvider mGpsLocationProvider;
131    private boolean mGpsNavigating;
132    private LocationProviderImpl mNetworkLocationProvider;
133    private INetworkLocationProvider mNetworkLocationInterface;
134    private LocationWorkerHandler mLocationHandler;
135
136    // Handler messages
137    private static final int MESSAGE_HEARTBEAT = 1;
138    private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
139    private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
140    private static final int MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER = 4;
141
142    // Alarm manager and wakelock variables
143    private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
144    private final static String WAKELOCK_KEY = "LocationManagerService";
145    private final static String WIFILOCK_KEY = "LocationManagerService";
146    private AlarmManager mAlarmManager;
147    private long mAlarmInterval = 0;
148    private boolean mScreenOn = true;
149    private PowerManager.WakeLock mWakeLock = null;
150    private WifiManager.WifiLock mWifiLock = null;
151    private long mWakeLockAcquireTime = 0;
152    private boolean mWakeLockGpsReceived = true;
153    private boolean mWakeLockNetworkReceived = true;
154    private boolean mWifiWakeLockAcquired = false;
155    private boolean mCellWakeLockAcquired = false;
156
157    private final IBatteryStats mBatteryStats;
158
159    /**
160     * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
161     */
162    private final ArrayList<Receiver> mListeners = new ArrayList<Receiver>();
163
164    /**
165     * Used for reporting which UIDs are causing the GPS to run.
166     */
167    private final SparseIntArray mReportedGpsUids = new SparseIntArray();
168    private int mReportedGpsSeq = 0;
169
170    /**
171     * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord.
172     * This also serves as the lock for our state.
173     */
174    private final HashMap<Receiver,HashMap<String,UpdateRecord>> mLocationListeners =
175        new HashMap<Receiver,HashMap<String,UpdateRecord>>();
176
177    /**
178     * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast
179     * location.
180     */
181    private final HashMap<Receiver,HashMap<String,Location>> mLastFixBroadcast =
182        new HashMap<Receiver,HashMap<String,Location>>();
183    private final HashMap<Receiver,HashMap<String,Long>> mLastStatusBroadcast =
184        new HashMap<Receiver,HashMap<String,Long>>();
185
186    /**
187     * Mapping from provider name to all its UpdateRecords
188     */
189    private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
190        new HashMap<String,ArrayList<UpdateRecord>>();
191
192    /**
193     * Mappings from provider name to object to use for current location. Locations
194     * contained in this list may not always be valid.
195     */
196    private final HashMap<String,Location> mLocationsByProvider =
197        new HashMap<String,Location>();
198
199    // Proximity listeners
200    private Receiver mProximityListener = null;
201    private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
202        new HashMap<PendingIntent,ProximityAlert>();
203    private HashSet<ProximityAlert> mProximitiesEntered =
204        new HashSet<ProximityAlert>();
205
206    // Last known location for each provider
207    private HashMap<String,Location> mLastKnownLocation =
208        new HashMap<String,Location>();
209
210    // Battery status extras (from com.android.server.BatteryService)
211    private static final String BATTERY_EXTRA_SCALE = "scale";
212    private static final String BATTERY_EXTRA_LEVEL = "level";
213    private static final String BATTERY_EXTRA_PLUGGED = "plugged";
214
215    // Last known cell service state
216    private TelephonyManager mTelephonyManager;
217    private int mSignalStrength = -1;
218
219    // Location collector
220    private ILocationCollector mCollector;
221
222    // Wifi Manager
223    private WifiManager mWifiManager;
224
225    /**
226     * A wrapper class holding either an ILocationListener or a PendingIntent to receive
227     * location updates.
228     */
229    private final class Receiver implements IBinder.DeathRecipient {
230        final ILocationListener mListener;
231        final PendingIntent mPendingIntent;
232        final int mUid;
233        final Object mKey;
234
235        Receiver(ILocationListener listener, int uid) {
236            mListener = listener;
237            mPendingIntent = null;
238            mUid = uid;
239            mKey = listener.asBinder();
240        }
241
242        Receiver(PendingIntent intent, int uid) {
243            mPendingIntent = intent;
244            mListener = null;
245            mUid = uid;
246            mKey = intent;
247        }
248
249        @Override
250        public boolean equals(Object otherObj) {
251            if (otherObj instanceof Receiver) {
252                return mKey.equals(
253                        ((Receiver)otherObj).mKey);
254            }
255            return false;
256        }
257
258        @Override
259        public int hashCode() {
260            return mKey.hashCode();
261        }
262
263
264        @Override
265        public String toString() {
266            if (mListener != null) {
267                return "Receiver{"
268                        + Integer.toHexString(System.identityHashCode(this))
269                        + " uid " + mUid + " Listener " + mKey + "}";
270            } else {
271                return "Receiver{"
272                        + Integer.toHexString(System.identityHashCode(this))
273                        + " uid " + mUid + " Intent " + mKey + "}";
274            }
275        }
276
277        public boolean isListener() {
278            return mListener != null;
279        }
280
281        public boolean isPendingIntent() {
282            return mPendingIntent != null;
283        }
284
285        public ILocationListener getListener() {
286            if (mListener != null) {
287                return mListener;
288            }
289            throw new IllegalStateException("Request for non-existent listener");
290        }
291
292        public PendingIntent getPendingIntent() {
293            if (mPendingIntent != null) {
294                return mPendingIntent;
295            }
296            throw new IllegalStateException("Request for non-existent intent");
297        }
298
299        public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
300            if (mListener != null) {
301                try {
302                    mListener.onStatusChanged(provider, status, extras);
303                } catch (RemoteException e) {
304                    return false;
305                }
306            } else {
307                Intent statusChanged = new Intent();
308                statusChanged.putExtras(extras);
309                statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
310                try {
311                    mPendingIntent.send(mContext, 0, statusChanged, null, null);
312                } catch (PendingIntent.CanceledException e) {
313                    return false;
314                }
315            }
316            return true;
317        }
318
319        public boolean callLocationChangedLocked(Location location) {
320            if (mListener != null) {
321                try {
322                    mListener.onLocationChanged(location);
323                } catch (RemoteException e) {
324                    return false;
325                }
326            } else {
327                Intent locationChanged = new Intent();
328                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
329                try {
330                    mPendingIntent.send(mContext, 0, locationChanged, null, null);
331                } catch (PendingIntent.CanceledException e) {
332                    return false;
333                }
334            }
335            return true;
336        }
337
338        public void binderDied() {
339            if (Config.LOGD) {
340                Log.d(TAG, "Location listener died");
341            }
342            synchronized (mLocationListeners) {
343                removeUpdatesLocked(this);
344            }
345        }
346    }
347
348    private Location readLastKnownLocationLocked(String provider) {
349        Location location = null;
350        String s = null;
351        try {
352            File f = new File(LocationManager.SYSTEM_DIR + "/location."
353                    + provider);
354            if (!f.exists()) {
355                return null;
356            }
357            BufferedReader reader = new BufferedReader(new FileReader(f), 256);
358            s = reader.readLine();
359        } catch (IOException e) {
360            Log.w(TAG, "Unable to read last known location", e);
361        }
362
363        if (s == null) {
364            return null;
365        }
366        try {
367            String[] tokens = PATTERN_COMMA.split(s);
368            int idx = 0;
369            long time = Long.parseLong(tokens[idx++]);
370            double latitude = Double.parseDouble(tokens[idx++]);
371            double longitude = Double.parseDouble(tokens[idx++]);
372            double altitude = Double.parseDouble(tokens[idx++]);
373            float bearing = Float.parseFloat(tokens[idx++]);
374            float speed = Float.parseFloat(tokens[idx++]);
375
376            location = new Location(provider);
377            location.setTime(time);
378            location.setLatitude(latitude);
379            location.setLongitude(longitude);
380            location.setAltitude(altitude);
381            location.setBearing(bearing);
382            location.setSpeed(speed);
383        } catch (NumberFormatException nfe) {
384            Log.e(TAG, "NumberFormatException reading last known location", nfe);
385            return null;
386        }
387
388        return location;
389    }
390
391    private void writeLastKnownLocationLocked(String provider,
392        Location location) {
393        long now = SystemClock.elapsedRealtime();
394        Long last = mLastWriteTime.get(provider);
395        if ((last != null)
396            && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
397            return;
398        }
399        mLastWriteTime.put(provider, now);
400
401        StringBuilder sb = new StringBuilder(100);
402        sb.append(location.getTime());
403        sb.append(',');
404        sb.append(location.getLatitude());
405        sb.append(',');
406        sb.append(location.getLongitude());
407        sb.append(',');
408        sb.append(location.getAltitude());
409        sb.append(',');
410        sb.append(location.getBearing());
411        sb.append(',');
412        sb.append(location.getSpeed());
413
414        FileWriter writer = null;
415        try {
416            File d = new File(LocationManager.SYSTEM_DIR);
417            if (!d.exists()) {
418                if (!d.mkdirs()) {
419                    Log.w(TAG, "Unable to create directory to write location");
420                    return;
421                }
422            }
423            File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
424            writer = new FileWriter(f);
425            writer.write(sb.toString());
426        } catch (IOException e) {
427            Log.w(TAG, "Unable to write location", e);
428        } finally {
429            if (writer != null) {
430                try {
431                writer.close();
432                } catch (IOException e) {
433                    Log.w(TAG, "Exception closing file", e);
434                }
435            }
436        }
437    }
438
439    /**
440     * Load providers from /data/location/<provider_name>/
441     *                                                          class
442     *                                                          kml
443     *                                                          nmea
444     *                                                          track
445     *                                                          location
446     *                                                          properties
447     */
448    private void loadProviders() {
449        synchronized (mLocationListeners) {
450            if (sProvidersLoaded) {
451                return;
452            }
453
454            // Load providers
455            loadProvidersLocked();
456            sProvidersLoaded = true;
457        }
458    }
459
460    private void loadProvidersLocked() {
461        try {
462            _loadProvidersLocked();
463        } catch (Exception e) {
464            Log.e(TAG, "Exception loading providers:", e);
465        }
466    }
467
468    private void _loadProvidersLocked() {
469        // Attempt to load "real" providers first
470        if (GpsLocationProvider.isSupported()) {
471            // Create a gps location provider
472            mGpsLocationProvider = new GpsLocationProvider(mContext);
473            LocationProviderImpl.addProvider(mGpsLocationProvider);
474        }
475
476        // Load fake providers if real providers are not available
477        File f = new File(LocationManager.PROVIDER_DIR);
478        if (f.isDirectory()) {
479            File[] subdirs = f.listFiles();
480            for (int i = 0; i < subdirs.length; i++) {
481                if (!subdirs[i].isDirectory()) {
482                    continue;
483                }
484
485                String name = subdirs[i].getName();
486
487                if (Config.LOGD) {
488                    Log.d(TAG, "Found dir " + subdirs[i].getAbsolutePath());
489                    Log.d(TAG, "name = " + name);
490                }
491
492                // Don't create a fake provider if a real provider exists
493                if (LocationProviderImpl.getProvider(name) == null) {
494                    LocationProviderImpl provider = null;
495                    try {
496                        File classFile = new File(subdirs[i], "class");
497                        // Look for a 'class' file
498                        provider = LocationProviderImpl.loadFromClass(classFile);
499
500                        // Look for an 'kml', 'nmea', or 'track' file
501                        if (provider == null) {
502                            // Load properties from 'properties' file, if present
503                            File propertiesFile = new File(subdirs[i], "properties");
504
505                            if (propertiesFile.exists()) {
506                                provider = new TrackProvider(name);
507                                ((TrackProvider)provider).readProperties(propertiesFile);
508
509                                File kmlFile = new File(subdirs[i], "kml");
510                                if (kmlFile.exists()) {
511                                    ((TrackProvider) provider).readKml(kmlFile);
512                                } else {
513                                    File nmeaFile = new File(subdirs[i], "nmea");
514                                    if (nmeaFile.exists()) {
515                                        ((TrackProvider) provider).readNmea(name, nmeaFile);
516                                    } else {
517                                        File trackFile = new File(subdirs[i], "track");
518                                        if (trackFile.exists()) {
519                                            ((TrackProvider) provider).readTrack(trackFile);
520                                        }
521                                    }
522                                }
523                            }
524                        }
525                        if (provider != null) {
526                            LocationProviderImpl.addProvider(provider);
527                        }
528                        // Grab the initial location of a TrackProvider and
529                        // store it as the last known location for that provider
530                        if (provider instanceof TrackProvider) {
531                            TrackProvider tp = (TrackProvider) provider;
532                            mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
533                        }
534                    } catch (Exception e) {
535                        Log.e(TAG, "Exception loading provder " + name, e);
536                    }
537                }
538            }
539        }
540
541        updateProvidersLocked();
542    }
543
544    /**
545     * @param context the context that the LocationManagerService runs in
546     */
547    public LocationManagerService(Context context) {
548        super();
549        mContext = context;
550        mLocationHandler = new LocationWorkerHandler();
551
552        if (Config.LOGD) {
553            Log.d(TAG, "Constructed LocationManager Service");
554        }
555
556        // Alarm manager, needs to be done before calling loadProviders() below
557        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
558
559        // Create a wake lock, needs to be done before calling loadProviders() below
560        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
561        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
562
563        // Battery statistics service to be notified when GPS turns on or off
564        mBatteryStats = BatteryStatsService.getService();
565
566        // Load providers
567        loadProviders();
568
569        // Listen for Radio changes
570        mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
571        mTelephonyManager.listen(mPhoneStateListener,
572                PhoneStateListener.LISTEN_CELL_LOCATION |
573                PhoneStateListener.LISTEN_SIGNAL_STRENGTH |
574                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
575
576        // Register for Network (Wifi or Mobile) updates
577        NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
578        IntentFilter networkIntentFilter = new IntentFilter();
579        networkIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
580        networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
581        networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
582        networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
583        context.registerReceiver(networkReceiver, networkIntentFilter);
584
585        // Register for power updates
586        PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
587        IntentFilter intentFilter = new IntentFilter();
588        intentFilter.addAction(ALARM_INTENT);
589        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
590        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
591        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
592        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
593        intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
594        context.registerReceiver(powerStateReceiver, intentFilter);
595
596        // Get the wifi manager
597        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
598
599        // Create a wifi lock for future use
600        mWifiLock = getWifiWakelockLocked();
601
602        // There might be an existing wifi scan available
603        if (mWifiManager != null) {
604            List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
605            if (wifiScanResults != null && wifiScanResults.size() != 0) {
606                if (mNetworkLocationInterface != null) {
607                    mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
608                }
609            }
610        }
611    }
612
613    public void setInstallCallback(InstallCallback callback) {
614        synchronized (mLocationListeners) {
615            mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER);
616            Message m = Message.obtain(mLocationHandler,
617                    MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback);
618            mLocationHandler.sendMessageAtFrontOfQueue(m);
619        }
620    }
621
622    public void setNetworkLocationProvider(INetworkLocationProvider provider) {
623        synchronized (mLocationListeners) {
624            mNetworkLocationInterface = provider;
625            provider.addListener(getPackageNames());
626            mNetworkLocationProvider = (LocationProviderImpl)provider;
627            LocationProviderImpl.addProvider(mNetworkLocationProvider);
628            updateProvidersLocked();
629        }
630    }
631
632    public void setLocationCollector(ILocationCollector collector) {
633        synchronized (mLocationListeners) {
634            mCollector = collector;
635            if (mGpsLocationProvider != null) {
636                mGpsLocationProvider.setLocationCollector(mCollector);
637            }
638        }
639    }
640
641    private WifiManager.WifiLock getWifiWakelockLocked() {
642        if (mWifiLock == null && mWifiManager != null) {
643            mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
644            mWifiLock.setReferenceCounted(false);
645        }
646        return mWifiLock;
647    }
648
649    private boolean isAllowedBySettingsLocked(String provider) {
650        if (mEnabledProviders.contains(provider)) {
651            return true;
652        }
653        if (mDisabledProviders.contains(provider)) {
654            return false;
655        }
656        // Use system settings
657        ContentResolver resolver = mContext.getContentResolver();
658        String allowedProviders = Settings.Secure.getString(resolver,
659           Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
660
661        return ((allowedProviders != null) && (allowedProviders.contains(provider)));
662    }
663
664    private void checkPermissionsSafe(String provider) {
665        if (LocationManager.GPS_PROVIDER.equals(provider)
666            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
667                != PackageManager.PERMISSION_GRANTED)) {
668            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
669        }
670        if (LocationManager.NETWORK_PROVIDER.equals(provider)
671            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
672                != PackageManager.PERMISSION_GRANTED)
673            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
674                != PackageManager.PERMISSION_GRANTED)) {
675            throw new SecurityException(
676                "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
677        }
678    }
679
680    private boolean isAllowedProviderSafe(String provider) {
681        if (LocationManager.GPS_PROVIDER.equals(provider)
682            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
683                != PackageManager.PERMISSION_GRANTED)) {
684            return false;
685        }
686        if (LocationManager.NETWORK_PROVIDER.equals(provider)
687            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
688                != PackageManager.PERMISSION_GRANTED)
689            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
690                != PackageManager.PERMISSION_GRANTED)) {
691            return false;
692        }
693
694        return true;
695    }
696
697    private String[] getPackageNames() {
698        // Since a single UID may correspond to multiple packages, this can only be used as an
699        // approximation for tracking
700        return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
701    }
702
703    public List<String> getAllProviders() {
704        try {
705            synchronized (mLocationListeners) {
706                return _getAllProvidersLocked();
707            }
708        } catch (SecurityException se) {
709            throw se;
710        } catch (Exception e) {
711            Log.e(TAG, "getAllProviders got exception:", e);
712            return null;
713        }
714    }
715
716    private List<String> _getAllProvidersLocked() {
717        if (Config.LOGD) {
718            Log.d(TAG, "getAllProviders");
719        }
720        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
721        ArrayList<String> out = new ArrayList<String>(providers.size());
722
723        for (LocationProviderImpl p : providers) {
724            out.add(p.getName());
725        }
726        return out;
727    }
728
729    public List<String> getProviders(boolean enabledOnly) {
730        try {
731            synchronized (mLocationListeners) {
732                return _getProvidersLocked(enabledOnly);
733            }
734        } catch (SecurityException se) {
735            throw se;
736        } catch (Exception e) {
737            Log.e(TAG, "getProviders gotString exception:", e);
738            return null;
739        }
740    }
741
742    private List<String> _getProvidersLocked(boolean enabledOnly) {
743        if (Config.LOGD) {
744            Log.d(TAG, "getProviders");
745        }
746        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
747        ArrayList<String> out = new ArrayList<String>();
748
749        for (LocationProviderImpl p : providers) {
750            String name = p.getName();
751            if (isAllowedProviderSafe(name)) {
752                if (enabledOnly && !isAllowedBySettingsLocked(name)) {
753                    continue;
754                }
755                out.add(name);
756            }
757        }
758        return out;
759    }
760
761    public void updateProviders() {
762        synchronized (mLocationListeners) {
763            updateProvidersLocked();
764        }
765    }
766
767    private void updateProvidersLocked() {
768        for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
769            boolean isEnabled = p.isEnabled();
770            String name = p.getName();
771            boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
772
773            // Collection is only allowed when network provider is being used
774            if (mCollector != null &&
775                    p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
776                mCollector.updateNetworkProviderStatus(shouldBeEnabled);
777            }
778
779            if (isEnabled && !shouldBeEnabled) {
780                updateProviderListenersLocked(name, false);
781            } else if (!isEnabled && shouldBeEnabled) {
782                updateProviderListenersLocked(name, true);
783            }
784
785        }
786    }
787
788    private void updateProviderListenersLocked(String provider, boolean enabled) {
789        int listeners = 0;
790
791        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
792        if (p == null) {
793            return;
794        }
795
796        ArrayList<Receiver> deadReceivers = null;
797
798        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
799        if (records != null) {
800            final int N = records.size();
801            for (int i=0; i<N; i++) {
802                UpdateRecord record = records.get(i);
803                // Sends a notification message to the receiver
804                try {
805                    Receiver receiver = record.mReceiver;
806                    if (receiver.isListener()) {
807                        if (enabled) {
808                            receiver.getListener().onProviderEnabled(provider);
809                        } else {
810                            receiver.getListener().onProviderDisabled(provider);
811                        }
812                    } else {
813                        Intent providerIntent = new Intent();
814                        providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
815                        try {
816                            receiver.getPendingIntent().send(mContext, 0,
817                                 providerIntent, null, null);
818                        } catch (PendingIntent.CanceledException e) {
819                            if (deadReceivers == null) {
820                                deadReceivers = new ArrayList<Receiver>();
821                                deadReceivers.add(receiver);
822                            }
823                        }
824                    }
825                } catch (RemoteException e) {
826                    // The death link will clean this up.
827                }
828                listeners++;
829            }
830        }
831
832        if (deadReceivers != null) {
833            for (int i=deadReceivers.size()-1; i>=0; i--) {
834                removeUpdatesLocked(deadReceivers.get(i));
835            }
836        }
837
838        if (enabled) {
839            p.enable();
840            if (listeners > 0) {
841                p.setMinTime(getMinTimeLocked(provider));
842                p.enableLocationTracking(true);
843                updateWakelockStatusLocked(mScreenOn);
844            }
845        } else {
846            p.enableLocationTracking(false);
847            if (p == mGpsLocationProvider) {
848                mGpsNavigating = false;
849                reportStopGpsLocked();
850            }
851            p.disable();
852            updateWakelockStatusLocked(mScreenOn);
853        }
854
855        if (enabled && listeners > 0) {
856            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
857            Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
858            mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
859        } else {
860            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
861        }
862    }
863
864    private long getMinTimeLocked(String provider) {
865        long minTime = Long.MAX_VALUE;
866        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
867        if (records != null) {
868            for (int i=records.size()-1; i>=0; i--) {
869                minTime = Math.min(minTime, records.get(i).mMinTime);
870            }
871        }
872        return minTime;
873    }
874
875    private class UpdateRecord {
876        final String mProvider;
877        final Receiver mReceiver;
878        final long mMinTime;
879        final float mMinDistance;
880        final int mUid;
881        final String[] mPackages;
882
883        /**
884         * Note: must be constructed with lock held.
885         */
886        UpdateRecord(String provider, long minTime, float minDistance,
887            Receiver receiver, int uid, String[] packages) {
888            mProvider = provider;
889            mReceiver = receiver;
890            mMinTime = minTime;
891            mMinDistance = minDistance;
892            mUid = uid;
893            mPackages = packages;
894
895            ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
896            if (records == null) {
897                records = new ArrayList<UpdateRecord>();
898                mRecordsByProvider.put(provider, records);
899            }
900            if (!records.contains(this)) {
901                records.add(this);
902            }
903        }
904
905        /**
906         * Method to be called when a record will no longer be used.  Calling this multiple times
907         * must have the same effect as calling it once.
908         */
909        void disposeLocked() {
910            ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
911            records.remove(this);
912        }
913
914        @Override
915        public String toString() {
916            return "UpdateRecord{"
917                    + Integer.toHexString(System.identityHashCode(this))
918                    + " " + mProvider + " " + mReceiver + "}";
919        }
920
921        void dump(PrintWriter pw, String prefix) {
922            pw.println(prefix + this);
923            pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
924            pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
925            StringBuilder sb = new StringBuilder();
926            if (mPackages != null) {
927                for (int i=0; i<mPackages.length; i++) {
928                    if (i > 0) sb.append(", ");
929                    sb.append(mPackages[i]);
930                }
931            }
932            pw.println(prefix + "mUid=" + mUid + " mPackages=" + sb);
933        }
934
935        /**
936         * Calls dispose().
937         */
938        @Override protected void finalize() {
939            synchronized (mLocationListeners) {
940                disposeLocked();
941            }
942        }
943    }
944
945    public void requestLocationUpdates(String provider,
946        long minTime, float minDistance, ILocationListener listener) {
947
948        try {
949            synchronized (mLocationListeners) {
950                requestLocationUpdatesLocked(provider, minTime, minDistance,
951                    new Receiver(listener, Binder.getCallingUid()));
952            }
953        } catch (SecurityException se) {
954            throw se;
955        } catch (Exception e) {
956            Log.e(TAG, "requestUpdates got exception:", e);
957        }
958    }
959
960    public void requestLocationUpdatesPI(String provider,
961            long minTime, float minDistance, PendingIntent intent) {
962        try {
963            synchronized (mLocationListeners) {
964                requestLocationUpdatesLocked(provider, minTime, minDistance,
965                        new Receiver(intent, Binder.getCallingUid()));
966            }
967        } catch (SecurityException se) {
968            throw se;
969        } catch (Exception e) {
970            Log.e(TAG, "requestUpdates got exception:", e);
971        }
972    }
973
974    private void requestLocationUpdatesLocked(String provider,
975            long minTime, float minDistance, Receiver receiver) {
976        if (Config.LOGD) {
977            Log.d(TAG, "_requestLocationUpdates: listener = " + receiver);
978        }
979
980        LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
981        if (impl == null) {
982            throw new IllegalArgumentException("provider=" + provider);
983        }
984
985        checkPermissionsSafe(provider);
986
987        String[] packages = getPackageNames();
988
989        // so wakelock calls will succeed
990        final int callingUid = Binder.getCallingUid();
991        long identity = Binder.clearCallingIdentity();
992        try {
993            UpdateRecord r = new UpdateRecord(provider, minTime, minDistance,
994                    receiver, callingUid, packages);
995            if (!mListeners.contains(receiver)) {
996                try {
997                    if (receiver.isListener()) {
998                        receiver.getListener().asBinder().linkToDeath(receiver, 0);
999                    }
1000                    mListeners.add(receiver);
1001                } catch (RemoteException e) {
1002                    return;
1003                }
1004            }
1005
1006            HashMap<String,UpdateRecord> records = mLocationListeners.get(receiver);
1007            if (records == null) {
1008                records = new HashMap<String,UpdateRecord>();
1009                mLocationListeners.put(receiver, records);
1010            }
1011            UpdateRecord oldRecord = records.put(provider, r);
1012            if (oldRecord != null) {
1013                oldRecord.disposeLocked();
1014            }
1015
1016            boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
1017            if (isProviderEnabled) {
1018                long minTimeForProvider = getMinTimeLocked(provider);
1019                impl.setMinTime(minTimeForProvider);
1020                impl.enableLocationTracking(true);
1021                updateWakelockStatusLocked(mScreenOn);
1022
1023                if (provider.equals(LocationManager.GPS_PROVIDER)) {
1024                    if (mGpsNavigating) {
1025                        updateReportedGpsLocked();
1026                    }
1027                }
1028
1029                // Clear heartbeats if any before starting a new one
1030                mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1031                Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
1032                mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1033            } else {
1034                try {
1035                    // Notify the listener that updates are currently disabled
1036                    if (receiver.isListener()) {
1037                        receiver.getListener().onProviderDisabled(provider);
1038                    }
1039                } catch(RemoteException e) {
1040                    Log.w(TAG, "RemoteException calling onProviderDisabled on " +
1041                            receiver.getListener());
1042                }
1043            }
1044        } finally {
1045            Binder.restoreCallingIdentity(identity);
1046        }
1047    }
1048
1049    public void removeUpdates(ILocationListener listener) {
1050        try {
1051            synchronized (mLocationListeners) {
1052                removeUpdatesLocked(new Receiver(listener, Binder.getCallingUid()));
1053            }
1054        } catch (SecurityException se) {
1055            throw se;
1056        } catch (Exception e) {
1057            Log.e(TAG, "removeUpdates got exception:", e);
1058        }
1059    }
1060
1061    public void removeUpdatesPI(PendingIntent intent) {
1062        try {
1063            synchronized (mLocationListeners) {
1064                removeUpdatesLocked(new Receiver(intent, Binder.getCallingUid()));
1065            }
1066        } catch (SecurityException se) {
1067            throw se;
1068        } catch (Exception e) {
1069            Log.e(TAG, "removeUpdates got exception:", e);
1070        }
1071    }
1072
1073    private void removeUpdatesLocked(Receiver receiver) {
1074        if (Config.LOGD) {
1075            Log.d(TAG, "_removeUpdates: listener = " + receiver);
1076        }
1077
1078        // so wakelock calls will succeed
1079        final int callingUid = Binder.getCallingUid();
1080        long identity = Binder.clearCallingIdentity();
1081        try {
1082            int idx = mListeners.indexOf(receiver);
1083            if (idx >= 0) {
1084                Receiver myReceiver = mListeners.remove(idx);
1085                if (myReceiver.isListener()) {
1086                    myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
1087                }
1088            }
1089
1090            // Record which providers were associated with this listener
1091            HashSet<String> providers = new HashSet<String>();
1092            HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(receiver);
1093            if (oldRecords != null) {
1094                // Call dispose() on the obsolete update records.
1095                for (UpdateRecord record : oldRecords.values()) {
1096                    if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
1097                        if (mNetworkLocationInterface != null) {
1098                            mNetworkLocationInterface.removeListener(record.mPackages);
1099                        }
1100                    }
1101                    record.disposeLocked();
1102                }
1103                // Accumulate providers
1104                providers.addAll(oldRecords.keySet());
1105            }
1106
1107            mLocationListeners.remove(receiver);
1108            mLastFixBroadcast.remove(receiver);
1109            mLastStatusBroadcast.remove(receiver);
1110
1111            // See if the providers associated with this listener have any
1112            // other listeners; if one does, inform it of the new smallest minTime
1113            // value; if one does not, disable location tracking for it
1114            for (String provider : providers) {
1115                // If provider is already disabled, don't need to do anything
1116                if (!isAllowedBySettingsLocked(provider)) {
1117                    continue;
1118                }
1119
1120                boolean hasOtherListener = false;
1121                ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1122                if (recordsForProvider != null && recordsForProvider.size() > 0) {
1123                    hasOtherListener = true;
1124                }
1125
1126                LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1127                if (p != null) {
1128                    if (hasOtherListener) {
1129                        p.setMinTime(getMinTimeLocked(provider));
1130                    } else {
1131                        mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1132                        p.enableLocationTracking(false);
1133                    }
1134
1135                    if (p == mGpsLocationProvider && mGpsNavigating) {
1136                        updateReportedGpsLocked();
1137                    }
1138                }
1139            }
1140
1141            updateWakelockStatusLocked(mScreenOn);
1142        } finally {
1143            Binder.restoreCallingIdentity(identity);
1144        }
1145    }
1146
1147    public boolean addGpsStatusListener(IGpsStatusListener listener) {
1148        if (mGpsLocationProvider == null) {
1149            return false;
1150        }
1151        if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1152                PackageManager.PERMISSION_GRANTED) {
1153            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1154        }
1155
1156        try {
1157            mGpsLocationProvider.addGpsStatusListener(listener);
1158        } catch (RemoteException e) {
1159            Log.w(TAG, "RemoteException in addGpsStatusListener");
1160            return false;
1161        }
1162        return true;
1163    }
1164
1165    public void removeGpsStatusListener(IGpsStatusListener listener) {
1166        synchronized (mLocationListeners) {
1167            mGpsLocationProvider.removeGpsStatusListener(listener);
1168        }
1169    }
1170
1171    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1172        // first check for permission to the provider
1173        checkPermissionsSafe(provider);
1174        // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1175        if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1176                != PackageManager.PERMISSION_GRANTED)) {
1177            throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1178        }
1179
1180        synchronized (mLocationListeners) {
1181            LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1182            if (provider == null) {
1183                return false;
1184            }
1185
1186            return impl.sendExtraCommand(command, extras);
1187        }
1188    }
1189
1190    class ProximityAlert {
1191        final int  mUid;
1192        final double mLatitude;
1193        final double mLongitude;
1194        final float mRadius;
1195        final long mExpiration;
1196        final PendingIntent mIntent;
1197        final Location mLocation;
1198
1199        public ProximityAlert(int uid, double latitude, double longitude,
1200            float radius, long expiration, PendingIntent intent) {
1201            mUid = uid;
1202            mLatitude = latitude;
1203            mLongitude = longitude;
1204            mRadius = radius;
1205            mExpiration = expiration;
1206            mIntent = intent;
1207
1208            mLocation = new Location("");
1209            mLocation.setLatitude(latitude);
1210            mLocation.setLongitude(longitude);
1211        }
1212
1213        long getExpiration() {
1214            return mExpiration;
1215        }
1216
1217        PendingIntent getIntent() {
1218            return mIntent;
1219        }
1220
1221        boolean isInProximity(double latitude, double longitude) {
1222            Location loc = new Location("");
1223            loc.setLatitude(latitude);
1224            loc.setLongitude(longitude);
1225
1226            double radius = loc.distanceTo(mLocation);
1227            return radius <= mRadius;
1228        }
1229
1230        @Override
1231        public String toString() {
1232            return "ProximityAlert{"
1233                    + Integer.toHexString(System.identityHashCode(this))
1234                    + " uid " + mUid + mIntent + "}";
1235        }
1236
1237        void dump(PrintWriter pw, String prefix) {
1238            pw.println(prefix + this);
1239            pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1240            pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1241            pw.println(prefix + "mIntent=" + mIntent);
1242            pw.println(prefix + "mLocation:");
1243            mLocation.dump(new PrintWriterPrinter(pw), prefix + "  ");
1244        }
1245    }
1246
1247    // Listener for receiving locations to trigger proximity alerts
1248    class ProximityListener extends ILocationListener.Stub {
1249
1250        boolean isGpsAvailable = false;
1251
1252        // Note: this is called with the lock held.
1253        public void onLocationChanged(Location loc) {
1254
1255            // If Gps is available, then ignore updates from NetworkLocationProvider
1256            if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1257                isGpsAvailable = true;
1258            }
1259            if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1260                return;
1261            }
1262
1263            // Process proximity alerts
1264            long now = System.currentTimeMillis();
1265            double latitude = loc.getLatitude();
1266            double longitude = loc.getLongitude();
1267            ArrayList<PendingIntent> intentsToRemove = null;
1268
1269            for (ProximityAlert alert : mProximityAlerts.values()) {
1270                PendingIntent intent = alert.getIntent();
1271                long expiration = alert.getExpiration();
1272
1273                if ((expiration == -1) || (now <= expiration)) {
1274                    boolean entered = mProximitiesEntered.contains(alert);
1275                    boolean inProximity =
1276                        alert.isInProximity(latitude, longitude);
1277                    if (!entered && inProximity) {
1278                        if (Config.LOGD) {
1279                            Log.i(TAG, "Entered alert");
1280                        }
1281                        mProximitiesEntered.add(alert);
1282                        Intent enteredIntent = new Intent();
1283                        enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1284                        try {
1285                            intent.send(mContext, 0, enteredIntent, null, null);
1286                        } catch (PendingIntent.CanceledException e) {
1287                            if (Config.LOGD) {
1288                                Log.i(TAG, "Canceled proximity alert: " + alert, e);
1289                            }
1290                            if (intentsToRemove == null) {
1291                                intentsToRemove = new ArrayList<PendingIntent>();
1292                            }
1293                            intentsToRemove.add(intent);
1294                        }
1295                    } else if (entered && !inProximity) {
1296                        if (Config.LOGD) {
1297                            Log.i(TAG, "Exited alert");
1298                        }
1299                        mProximitiesEntered.remove(alert);
1300                        Intent exitedIntent = new Intent();
1301                        exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1302                        try {
1303                            intent.send(mContext, 0, exitedIntent, null, null);
1304                        } catch (PendingIntent.CanceledException e) {
1305                            if (Config.LOGD) {
1306                                Log.i(TAG, "Canceled proximity alert: " + alert, e);
1307                            }
1308                            if (intentsToRemove == null) {
1309                                intentsToRemove = new ArrayList<PendingIntent>();
1310                            }
1311                            intentsToRemove.add(intent);
1312                        }
1313                    }
1314                } else {
1315                    // Mark alert for expiration
1316                    if (Config.LOGD) {
1317                        Log.i(TAG, "Expiring proximity alert: " + alert);
1318                    }
1319                    if (intentsToRemove == null) {
1320                        intentsToRemove = new ArrayList<PendingIntent>();
1321                    }
1322                    intentsToRemove.add(alert.getIntent());
1323                }
1324            }
1325
1326            // Remove expired alerts
1327            if (intentsToRemove != null) {
1328                for (PendingIntent i : intentsToRemove) {
1329                    mProximityAlerts.remove(i);
1330                    ProximityAlert alert = mProximityAlerts.get(i);
1331                    mProximitiesEntered.remove(alert);
1332                }
1333            }
1334
1335        }
1336
1337        // Note: this is called with the lock held.
1338        public void onProviderDisabled(String provider) {
1339            if (provider.equals(LocationManager.GPS_PROVIDER)) {
1340                isGpsAvailable = false;
1341            }
1342        }
1343
1344        // Note: this is called with the lock held.
1345        public void onProviderEnabled(String provider) {
1346            // ignore
1347        }
1348
1349        // Note: this is called with the lock held.
1350        public void onStatusChanged(String provider, int status, Bundle extras) {
1351            if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1352                (status != LocationProvider.AVAILABLE)) {
1353                isGpsAvailable = false;
1354            }
1355        }
1356    }
1357
1358    public void addProximityAlert(double latitude, double longitude,
1359        float radius, long expiration, PendingIntent intent) {
1360        try {
1361            synchronized (mLocationListeners) {
1362                addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1363            }
1364        } catch (SecurityException se) {
1365            throw se;
1366        } catch (Exception e) {
1367            Log.e(TAG, "addProximityAlert got exception:", e);
1368        }
1369    }
1370
1371    private void addProximityAlertLocked(double latitude, double longitude,
1372        float radius, long expiration, PendingIntent intent) {
1373        if (Config.LOGD) {
1374            Log.d(TAG, "addProximityAlert: latitude = " + latitude +
1375                    ", longitude = " + longitude +
1376                    ", expiration = " + expiration +
1377                    ", intent = " + intent);
1378        }
1379
1380        // Require ability to access all providers for now
1381        if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1382            !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1383            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1384        }
1385
1386        if (expiration != -1) {
1387            expiration += System.currentTimeMillis();
1388        }
1389        ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1390                latitude, longitude, radius, expiration, intent);
1391        mProximityAlerts.put(intent, alert);
1392
1393        if (mProximityListener == null) {
1394            mProximityListener = new Receiver(new ProximityListener(), -1);
1395
1396            LocationProvider provider = LocationProviderImpl.getProvider(
1397                LocationManager.GPS_PROVIDER);
1398            if (provider != null) {
1399                requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1400            }
1401
1402            provider =
1403                LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1404            if (provider != null) {
1405                requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1406            }
1407        } else if (mGpsNavigating) {
1408            updateReportedGpsLocked();
1409        }
1410    }
1411
1412    public void removeProximityAlert(PendingIntent intent) {
1413        try {
1414            synchronized (mLocationListeners) {
1415               removeProximityAlertLocked(intent);
1416            }
1417        } catch (SecurityException se) {
1418            throw se;
1419        } catch (Exception e) {
1420            Log.e(TAG, "removeProximityAlert got exception:", e);
1421        }
1422    }
1423
1424    private void removeProximityAlertLocked(PendingIntent intent) {
1425        if (Config.LOGD) {
1426            Log.d(TAG, "removeProximityAlert: intent = " + intent);
1427        }
1428
1429        mProximityAlerts.remove(intent);
1430        if (mProximityAlerts.size() == 0) {
1431            removeUpdatesLocked(mProximityListener);
1432            mProximityListener = null;
1433        } else if (mGpsNavigating) {
1434            updateReportedGpsLocked();
1435        }
1436     }
1437
1438    /**
1439     * @return null if the provider does not exits
1440     * @throw SecurityException if the provider is not allowed to be
1441     * accessed by the caller
1442     */
1443    public Bundle getProviderInfo(String provider) {
1444        try {
1445            synchronized (mLocationListeners) {
1446                return _getProviderInfoLocked(provider);
1447            }
1448        } catch (SecurityException se) {
1449            throw se;
1450        } catch (Exception e) {
1451            Log.e(TAG, "_getProviderInfo got exception:", e);
1452            return null;
1453        }
1454    }
1455
1456    private Bundle _getProviderInfoLocked(String provider) {
1457        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1458        if (p == null) {
1459            return null;
1460        }
1461
1462        checkPermissionsSafe(provider);
1463
1464        Bundle b = new Bundle();
1465        b.putBoolean("network", p.requiresNetwork());
1466        b.putBoolean("satellite", p.requiresSatellite());
1467        b.putBoolean("cell", p.requiresCell());
1468        b.putBoolean("cost", p.hasMonetaryCost());
1469        b.putBoolean("altitude", p.supportsAltitude());
1470        b.putBoolean("speed", p.supportsSpeed());
1471        b.putBoolean("bearing", p.supportsBearing());
1472        b.putInt("power", p.getPowerRequirement());
1473        b.putInt("accuracy", p.getAccuracy());
1474
1475        return b;
1476    }
1477
1478    public boolean isProviderEnabled(String provider) {
1479        try {
1480            synchronized (mLocationListeners) {
1481                return _isProviderEnabledLocked(provider);
1482            }
1483        } catch (SecurityException se) {
1484            throw se;
1485        } catch (Exception e) {
1486            Log.e(TAG, "isProviderEnabled got exception:", e);
1487            return false;
1488        }
1489    }
1490
1491    private boolean _isProviderEnabledLocked(String provider) {
1492        checkPermissionsSafe(provider);
1493
1494        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1495        if (p == null) {
1496            throw new IllegalArgumentException("provider=" + provider);
1497        }
1498        return isAllowedBySettingsLocked(provider);
1499    }
1500
1501    public Location getLastKnownLocation(String provider) {
1502        try {
1503            synchronized (mLocationListeners) {
1504                return _getLastKnownLocationLocked(provider);
1505            }
1506        } catch (SecurityException se) {
1507            throw se;
1508        } catch (Exception e) {
1509            Log.e(TAG, "getLastKnownLocation got exception:", e);
1510            return null;
1511        }
1512    }
1513
1514    private Location _getLastKnownLocationLocked(String provider) {
1515        checkPermissionsSafe(provider);
1516
1517        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1518        if (p == null) {
1519            throw new IllegalArgumentException("provider=" + provider);
1520        }
1521
1522        if (!isAllowedBySettingsLocked(provider)) {
1523            return null;
1524        }
1525
1526        Location location = mLastKnownLocation.get(provider);
1527        if (location == null) {
1528            // Get the persistent last known location for the provider
1529            location = readLastKnownLocationLocked(provider);
1530            if (location != null) {
1531                mLastKnownLocation.put(provider, location);
1532            }
1533        }
1534
1535        return location;
1536    }
1537
1538    private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1539        // Always broadcast the first update
1540        if (lastLoc == null) {
1541            return true;
1542        }
1543
1544        // Don't broadcast same location again regardless of condition
1545        // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1546        if (loc.getTime() == lastLoc.getTime()) {
1547            return false;
1548        }
1549
1550        // Check whether sufficient distance has been traveled
1551        double minDistance = record.mMinDistance;
1552        if (minDistance > 0.0) {
1553            if (loc.distanceTo(lastLoc) <= minDistance) {
1554                return false;
1555            }
1556        }
1557
1558        return true;
1559    }
1560
1561    private void handleLocationChangedLocked(String provider) {
1562        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1563        if (records == null || records.size() == 0) {
1564            return;
1565        }
1566
1567        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1568        if (p == null) {
1569            return;
1570        }
1571
1572        // Get location object
1573        Location loc = mLocationsByProvider.get(provider);
1574        if (loc == null) {
1575            loc = new Location(provider);
1576            mLocationsByProvider.put(provider, loc);
1577        } else {
1578            loc.reset();
1579        }
1580
1581        // Use the mock location if available
1582        Location mockLoc = mMockProviderLocation.get(provider);
1583        boolean locationValid;
1584        if (mockLoc != null) {
1585            locationValid = true;
1586            loc.set(mockLoc);
1587        } else {
1588            locationValid = p.getLocation(loc);
1589        }
1590
1591        // Update last known location for provider
1592        if (locationValid) {
1593            Location location = mLastKnownLocation.get(provider);
1594            if (location == null) {
1595                mLastKnownLocation.put(provider, new Location(loc));
1596            } else {
1597                location.set(loc);
1598            }
1599            writeLastKnownLocationLocked(provider, loc);
1600
1601            if (p instanceof INetworkLocationProvider) {
1602                mWakeLockNetworkReceived = true;
1603            } else if (p instanceof GpsLocationProvider) {
1604                // Gps location received signal is in NetworkStateBroadcastReceiver
1605            }
1606        }
1607
1608        // Fetch latest status update time
1609        long newStatusUpdateTime = p.getStatusUpdateTime();
1610
1611        // Override real time with mock time if present
1612        Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider);
1613        if (mockStatusUpdateTime != null) {
1614            newStatusUpdateTime = mockStatusUpdateTime.longValue();
1615        }
1616
1617        // Get latest status
1618        Bundle extras = new Bundle();
1619        int status = p.getStatus(extras);
1620
1621        // Override status with mock status if present
1622        Integer mockStatus = mMockProviderStatus.get(provider);
1623        if (mockStatus != null) {
1624            status = mockStatus.intValue();
1625        }
1626
1627        // Override extras with mock extras if present
1628        Bundle mockExtras = mMockProviderStatusExtras.get(provider);
1629        if (mockExtras != null) {
1630            extras.clear();
1631            extras.putAll(mockExtras);
1632        }
1633
1634        ArrayList<Receiver> deadReceivers = null;
1635
1636        // Broadcast location or status to all listeners
1637        final int N = records.size();
1638        for (int i=0; i<N; i++) {
1639            UpdateRecord r = records.get(i);
1640            Receiver receiver = r.mReceiver;
1641
1642            // Broadcast location only if it is valid
1643            if (locationValid) {
1644                HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
1645                if (map == null) {
1646                    map = new HashMap<String,Location>();
1647                    mLastFixBroadcast.put(receiver, map);
1648                }
1649                Location lastLoc = map.get(provider);
1650                if ((lastLoc == null) || shouldBroadcastSafe(loc, lastLoc, r)) {
1651                    if (lastLoc == null) {
1652                        lastLoc = new Location(loc);
1653                        map.put(provider, lastLoc);
1654                    } else {
1655                        lastLoc.set(loc);
1656                    }
1657                    if (!receiver.callLocationChangedLocked(loc)) {
1658                        Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1659                        if (deadReceivers == null) {
1660                            deadReceivers = new ArrayList<Receiver>();
1661                        }
1662                        deadReceivers.add(receiver);
1663                    }
1664                }
1665            }
1666
1667            // Broadcast status message
1668            HashMap<String,Long> statusMap = mLastStatusBroadcast.get(receiver);
1669            if (statusMap == null) {
1670                statusMap = new HashMap<String,Long>();
1671                mLastStatusBroadcast.put(receiver, statusMap);
1672            }
1673            long prevStatusUpdateTime =
1674                (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1675
1676            if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1677                (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1678
1679                statusMap.put(provider, newStatusUpdateTime);
1680                if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1681                    Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1682                    if (deadReceivers == null) {
1683                        deadReceivers = new ArrayList<Receiver>();
1684                    }
1685                    if (!deadReceivers.contains(receiver)) {
1686                        deadReceivers.add(receiver);
1687                    }
1688                }
1689            }
1690        }
1691
1692        if (deadReceivers != null) {
1693            for (int i=deadReceivers.size()-1; i>=0; i--) {
1694                removeUpdatesLocked(deadReceivers.get(i));
1695            }
1696        }
1697    }
1698
1699    private class LocationWorkerHandler extends Handler {
1700
1701        @Override
1702        public void handleMessage(Message msg) {
1703            try {
1704                if (msg.what == MESSAGE_HEARTBEAT) {
1705                    // log("LocationWorkerHandler: Heartbeat!");
1706
1707                    synchronized (mLocationListeners) {
1708                        String provider = (String) msg.obj;
1709                        if (!isAllowedBySettingsLocked(provider)) {
1710                            return;
1711                        }
1712
1713                        // Process the location fix if the screen is on or we're holding a wakelock
1714                        if (mScreenOn || (mWakeLockAcquireTime != 0)) {
1715                            handleLocationChangedLocked(provider);
1716                        }
1717
1718                        // If it continues to have listeners
1719                        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1720                        if (records != null && records.size() > 0) {
1721                            Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
1722                            sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1723                        }
1724
1725                        if ((mWakeLockAcquireTime != 0) &&
1726                            (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1727                                > MAX_TIME_FOR_WAKE_LOCK)) {
1728
1729                            removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1730                            removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1731
1732                            log("LocationWorkerHandler: Exceeded max time for wake lock");
1733                            Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1734                            sendMessageAtFrontOfQueue(m);
1735
1736                        } else if (mWakeLockAcquireTime != 0 &&
1737                            mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1738
1739                            removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1740                            removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1741
1742                            log("LocationWorkerHandler: Locations received.");
1743                            mWakeLockAcquireTime = 0;
1744                            Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1745                            sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1746                        }
1747                    }
1748
1749                } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1750                    log("LocationWorkerHandler: Acquire");
1751                    synchronized (mLocationListeners) {
1752                        acquireWakeLockLocked();
1753                    }
1754                } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1755                    log("LocationWorkerHandler: Release");
1756
1757                    // Update wakelock status so the next alarm is set before releasing wakelock
1758                    synchronized (mLocationListeners) {
1759                        updateWakelockStatusLocked(mScreenOn);
1760                        releaseWakeLockLocked();
1761                    }
1762                } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) {
1763                    synchronized (mLocationListeners) {
1764                        Log.d(TAG, "installing network location provider");
1765                        INetworkLocationManager.InstallCallback callback =
1766                                (INetworkLocationManager.InstallCallback)msg.obj;
1767                        callback.installNetworkLocationProvider(mContext, LocationManagerService.this);
1768                    }
1769                }
1770            } catch (Exception e) {
1771                // Log, don't crash!
1772                Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1773            }
1774        }
1775    }
1776
1777    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1778
1779        private CellState mLastCellState = null;
1780        @Override
1781        public void onCellLocationChanged(CellLocation cellLocation) {
1782            try {
1783                synchronized (mLocationListeners) {
1784                    int asu = mSignalStrength;
1785
1786                    // Gets cell state
1787                    mLastCellState = new CellState(mTelephonyManager, cellLocation, asu);
1788
1789                    // Notify collector
1790                    if (mCollector != null) {
1791                        mCollector.updateCellState(mLastCellState);
1792                    }
1793
1794                    // Updates providers
1795                    List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1796                    for (LocationProviderImpl provider : providers) {
1797                        if (provider.requiresCell()) {
1798                            provider.updateCellState(mLastCellState);
1799                        }
1800                    }
1801                }
1802            } catch (Exception e) {
1803                Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e);
1804            }
1805        }
1806
1807        @Override
1808        public void onSignalStrengthChanged(int asu) {
1809            synchronized (mLocationListeners) {
1810                mSignalStrength = asu;
1811
1812                if (mLastCellState != null) {
1813                    mLastCellState.updateSignalStrength(asu);
1814                }
1815            }
1816        }
1817
1818        @Override
1819        public void onDataConnectionStateChanged(int state) {
1820            synchronized (mLocationListeners) {
1821                if (mLastCellState != null) {
1822                    mLastCellState.updateRadioType(mTelephonyManager);
1823                }
1824            }
1825        }
1826    };
1827
1828    private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1829        @Override public void onReceive(Context context, Intent intent) {
1830            String action = intent.getAction();
1831
1832            if (action.equals(ALARM_INTENT)) {
1833                synchronized (mLocationListeners) {
1834                    log("PowerStateBroadcastReceiver: Alarm received");
1835                    mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1836                    // Have to do this immediately, rather than posting a
1837                    // message, so we execute our code while the system
1838                    // is holding a wake lock until the alarm broadcast
1839                    // is finished.
1840                    acquireWakeLockLocked();
1841                }
1842
1843            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1844                log("PowerStateBroadcastReceiver: Screen off");
1845                synchronized (mLocationListeners) {
1846                    updateWakelockStatusLocked(false);
1847                }
1848
1849            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1850                log("PowerStateBroadcastReceiver: Screen on");
1851                synchronized (mLocationListeners) {
1852                    updateWakelockStatusLocked(true);
1853                }
1854
1855            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1856                log("PowerStateBroadcastReceiver: Battery changed");
1857                synchronized (mLocationListeners) {
1858                    int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
1859                    int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
1860                    boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
1861
1862                    // Notify collector battery state
1863                    if (mCollector != null) {
1864                        mCollector.updateBatteryState(scale, level, plugged);
1865                    }
1866                }
1867            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1868                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
1869                synchronized (mLocationListeners) {
1870                    int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1871                    if (uid >= 0) {
1872                        ArrayList<Receiver> removedRecs = null;
1873                        for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1874                            for (int j=i.size()-1; j>=0; j--) {
1875                                UpdateRecord ur = i.get(j);
1876                                if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1877                                    if (removedRecs == null) {
1878                                        removedRecs = new ArrayList<Receiver>();
1879                                    }
1880                                    if (!removedRecs.contains(ur.mReceiver)) {
1881                                        removedRecs.add(ur.mReceiver);
1882                                    }
1883                                }
1884                            }
1885                        }
1886                        ArrayList<ProximityAlert> removedAlerts = null;
1887                        for (ProximityAlert i : mProximityAlerts.values()) {
1888                            if (i.mUid == uid) {
1889                                if (removedAlerts == null) {
1890                                    removedAlerts = new ArrayList<ProximityAlert>();
1891                                }
1892                                if (!removedAlerts.contains(i)) {
1893                                    removedAlerts.add(i);
1894                                }
1895                            }
1896                        }
1897                        if (removedRecs != null) {
1898                            for (int i=removedRecs.size()-1; i>=0; i--) {
1899                                removeUpdatesLocked(removedRecs.get(i));
1900                            }
1901                        }
1902                        if (removedAlerts != null) {
1903                            for (int i=removedAlerts.size()-1; i>=0; i--) {
1904                                removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1905                            }
1906                        }
1907                    }
1908                }
1909            }
1910        }
1911    }
1912
1913    private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1914        @Override public void onReceive(Context context, Intent intent) {
1915            String action = intent.getAction();
1916
1917            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
1918
1919                List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
1920
1921                if (wifiScanResults == null) {
1922                    return;
1923                }
1924
1925                // Notify provider and collector of Wifi scan results
1926                synchronized (mLocationListeners) {
1927                    if (mCollector != null) {
1928                        mCollector.updateWifiScanResults(wifiScanResults);
1929                    }
1930                    if (mNetworkLocationInterface != null) {
1931                        mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
1932                    }
1933                }
1934
1935            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1936                int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
1937
1938                boolean noConnectivity =
1939                    intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1940                if (!noConnectivity) {
1941                    networkState = LocationProvider.AVAILABLE;
1942                }
1943
1944                // Notify location providers of current network state
1945                synchronized (mLocationListeners) {
1946                    List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1947                    for (LocationProviderImpl provider : providers) {
1948                        if (provider.requiresNetwork()) {
1949                            provider.updateNetworkState(networkState);
1950                        }
1951                    }
1952                }
1953
1954            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1955                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1956                    WifiManager.WIFI_STATE_UNKNOWN);
1957
1958                boolean enabled;
1959                if (state == WifiManager.WIFI_STATE_ENABLED) {
1960                    enabled = true;
1961                } else if (state == WifiManager.WIFI_STATE_DISABLED) {
1962                    enabled = false;
1963                } else {
1964                    return;
1965                }
1966
1967                // Notify network provider of current wifi enabled state
1968                synchronized (mLocationListeners) {
1969                    if (mNetworkLocationInterface != null) {
1970                        mNetworkLocationInterface.updateWifiEnabledState(enabled);
1971                    }
1972                }
1973
1974            } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1975
1976                final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1977                    false);
1978
1979                synchronized (mLocationListeners) {
1980                    if (enabled) {
1981                        updateReportedGpsLocked();
1982                        mGpsNavigating = true;
1983                    } else {
1984                        reportStopGpsLocked();
1985                        mGpsNavigating = false;
1986                        // When GPS is disabled, we are OK to release wake-lock
1987                        mWakeLockGpsReceived = true;
1988                    }
1989                }
1990            }
1991
1992        }
1993    }
1994
1995    // Wake locks
1996
1997    private void updateWakelockStatusLocked(boolean screenOn) {
1998        log("updateWakelockStatus(): " + screenOn);
1999
2000        boolean needsLock = false;
2001        long minTime = Integer.MAX_VALUE;
2002
2003        if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
2004            needsLock = true;
2005            minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
2006        }
2007
2008        if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
2009            needsLock = true;
2010            minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
2011            if (screenOn) {
2012                startGpsLocked();
2013            } else if (mScreenOn && !screenOn) {
2014                // We just turned the screen off so stop navigating
2015                stopGpsLocked();
2016            }
2017        }
2018
2019        mScreenOn = screenOn;
2020
2021        PendingIntent sender =
2022            PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
2023
2024        // Cancel existing alarm
2025        log("Cancelling existing alarm");
2026        mAlarmManager.cancel(sender);
2027
2028        if (needsLock && !mScreenOn) {
2029            long now = SystemClock.elapsedRealtime();
2030            mAlarmManager.set(
2031                AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
2032            mAlarmInterval = minTime;
2033            log("Creating a new wakelock alarm with minTime = " + minTime);
2034        } else {
2035            log("No need for alarm");
2036            mAlarmInterval = -1;
2037
2038            // Clear out existing wakelocks
2039            mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
2040            mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
2041            releaseWakeLockLocked();
2042        }
2043    }
2044
2045    private void acquireWakeLockLocked() {
2046        try {
2047            acquireWakeLockXLocked();
2048        } catch (Exception e) {
2049            // This is to catch a runtime exception thrown when we try to release an
2050            // already released lock.
2051            Log.e(TAG, "exception in acquireWakeLock()", e);
2052        }
2053    }
2054
2055    private void acquireWakeLockXLocked() {
2056        if (mWakeLock.isHeld()) {
2057            log("Must release wakelock before acquiring");
2058            mWakeLockAcquireTime = 0;
2059            mWakeLock.release();
2060        }
2061
2062        boolean networkActive = (mNetworkLocationProvider != null)
2063                && mNetworkLocationProvider.isLocationTracking();
2064        boolean gpsActive = (mGpsLocationProvider != null)
2065                && mGpsLocationProvider.isLocationTracking();
2066
2067        boolean needsLock = networkActive || gpsActive;
2068        if (!needsLock) {
2069            log("No need for Lock!");
2070            return;
2071        }
2072
2073        mWakeLockGpsReceived = !gpsActive;
2074        mWakeLockNetworkReceived = !networkActive;
2075
2076        // Acquire wake lock
2077        mWakeLock.acquire();
2078        mWakeLockAcquireTime = SystemClock.elapsedRealtime();
2079        log("Acquired wakelock");
2080
2081        // Start the gps provider
2082        startGpsLocked();
2083
2084        // Acquire cell lock
2085        if (mCellWakeLockAcquired) {
2086            // Lock is already acquired
2087        } else if (!mWakeLockNetworkReceived) {
2088            mTelephonyManager.enableLocationUpdates();
2089            mCellWakeLockAcquired = true;
2090        } else {
2091            mCellWakeLockAcquired = false;
2092        }
2093
2094        // Notify NetworkLocationProvider
2095        if (mNetworkLocationInterface != null) {
2096            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
2097        }
2098
2099        // Acquire wifi lock
2100        WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
2101        if (wifiLock != null) {
2102            if (mWifiWakeLockAcquired) {
2103                // Lock is already acquired
2104            } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
2105                wifiLock.acquire();
2106                mWifiWakeLockAcquired = true;
2107            } else {
2108                mWifiWakeLockAcquired = false;
2109                Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
2110            }
2111        }
2112    }
2113
2114    private boolean reportGpsUidLocked(int curSeq, int nextSeq, int uid) {
2115        int seq = mReportedGpsUids.get(uid, -1);
2116        if (seq == curSeq) {
2117            // Already reported; propagate to next sequence.
2118            mReportedGpsUids.put(uid, nextSeq);
2119            return true;
2120        } else if (seq != nextSeq) {
2121            try {
2122                // New UID; report it.
2123                mBatteryStats.noteStartGps(uid);
2124                mReportedGpsUids.put(uid, nextSeq);
2125                return true;
2126            } catch (RemoteException e) {
2127            }
2128        }
2129        return false;
2130    }
2131
2132    private void updateReportedGpsLocked() {
2133        if (mGpsLocationProvider == null) {
2134            return;
2135        }
2136
2137        final String name = mGpsLocationProvider.getName();
2138        final int curSeq = mReportedGpsSeq;
2139        final int nextSeq = (curSeq+1) >= 0 ? (curSeq+1) : 0;
2140        mReportedGpsSeq = nextSeq;
2141
2142        ArrayList<UpdateRecord> urs = mRecordsByProvider.get(name);
2143        int num = 0;
2144        final int N = urs.size();
2145        for (int i=0; i<N; i++) {
2146            UpdateRecord ur = urs.get(i);
2147            if (ur.mReceiver == mProximityListener) {
2148                // We don't want the system to take the blame for this one.
2149                continue;
2150            }
2151            if (reportGpsUidLocked(curSeq, nextSeq, ur.mUid)) {
2152                num++;
2153            }
2154        }
2155
2156        for (ProximityAlert pe : mProximityAlerts.values()) {
2157            if (reportGpsUidLocked(curSeq, nextSeq, pe.mUid)) {
2158                num++;
2159            }
2160        }
2161
2162        if (num != mReportedGpsUids.size()) {
2163            // The number of uids is processed is different than the
2164            // array; report any that are no longer active.
2165            for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
2166                if (mReportedGpsUids.valueAt(i) != nextSeq) {
2167                    try {
2168                        mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
2169                    } catch (RemoteException e) {
2170                    }
2171                    mReportedGpsUids.removeAt(i);
2172                }
2173            }
2174        }
2175    }
2176
2177    private void reportStopGpsLocked() {
2178        int curSeq = mReportedGpsSeq;
2179        for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
2180            if (mReportedGpsUids.valueAt(i) == curSeq) {
2181                try {
2182                    mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
2183                } catch (RemoteException e) {
2184                }
2185            }
2186        }
2187        curSeq++;
2188        if (curSeq < 0) curSeq = 0;
2189        mReportedGpsSeq = curSeq;
2190        mReportedGpsUids.clear();
2191    }
2192
2193    private void startGpsLocked() {
2194        boolean gpsActive = (mGpsLocationProvider != null)
2195                    && mGpsLocationProvider.isLocationTracking();
2196        if (gpsActive) {
2197            mGpsLocationProvider.startNavigating();
2198        }
2199    }
2200
2201    private void stopGpsLocked() {
2202        boolean gpsActive = mGpsLocationProvider != null
2203                    && mGpsLocationProvider.isLocationTracking();
2204        if (gpsActive) {
2205            mGpsLocationProvider.stopNavigating();
2206        }
2207    }
2208
2209    private void releaseWakeLockLocked() {
2210        try {
2211            releaseWakeLockXLocked();
2212        } catch (Exception e) {
2213            // This is to catch a runtime exception thrown when we try to release an
2214            // already released lock.
2215            Log.e(TAG, "exception in releaseWakeLock()", e);
2216        }
2217    }
2218
2219    private void releaseWakeLockXLocked() {
2220        // Release wifi lock
2221        WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
2222        if (wifiLock != null) {
2223            if (mWifiWakeLockAcquired) {
2224                wifiLock.release();
2225                mWifiWakeLockAcquired = false;
2226            }
2227        }
2228
2229        if (!mScreenOn) {
2230            // Stop the gps
2231            stopGpsLocked();
2232        }
2233
2234        // Release cell lock
2235        if (mCellWakeLockAcquired) {
2236            mTelephonyManager.disableLocationUpdates();
2237            mCellWakeLockAcquired = false;
2238        }
2239
2240        // Notify NetworkLocationProvider
2241        if (mNetworkLocationInterface != null) {
2242            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
2243        }
2244
2245        // Release wake lock
2246        mWakeLockAcquireTime = 0;
2247        if (mWakeLock.isHeld()) {
2248            log("Released wakelock");
2249            mWakeLock.release();
2250        } else {
2251            log("Can't release wakelock again!");
2252        }
2253    }
2254
2255    // Geocoder
2256
2257    public String getFromLocation(double latitude, double longitude, int maxResults,
2258        String language, String country, String variant, String appName, List<Address> addrs) {
2259        synchronized (mLocationListeners) {
2260            if (mNetworkLocationInterface != null) {
2261                return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults,
2262                        language, country, variant, appName, addrs);
2263            } else {
2264                return null;
2265            }
2266        }
2267    }
2268
2269    public String getFromLocationName(String locationName,
2270        double lowerLeftLatitude, double lowerLeftLongitude,
2271        double upperRightLatitude, double upperRightLongitude, int maxResults,
2272        String language, String country, String variant, String appName, List<Address> addrs) {
2273        synchronized (mLocationListeners) {
2274            if (mNetworkLocationInterface != null) {
2275                return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude,
2276                        lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
2277                        language, country, variant, appName, addrs);
2278            } else {
2279                return null;
2280            }
2281        }
2282    }
2283
2284    // Mock Providers
2285
2286    class MockProvider extends LocationProviderImpl {
2287        boolean mRequiresNetwork;
2288        boolean mRequiresSatellite;
2289        boolean mRequiresCell;
2290        boolean mHasMonetaryCost;
2291        boolean mSupportsAltitude;
2292        boolean mSupportsSpeed;
2293        boolean mSupportsBearing;
2294        int mPowerRequirement;
2295        int mAccuracy;
2296
2297        public MockProvider(String name,  boolean requiresNetwork, boolean requiresSatellite,
2298            boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2299            boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2300            super(name);
2301
2302            mRequiresNetwork = requiresNetwork;
2303            mRequiresSatellite = requiresSatellite;
2304            mRequiresCell = requiresCell;
2305            mHasMonetaryCost = hasMonetaryCost;
2306            mSupportsAltitude = supportsAltitude;
2307            mSupportsBearing = supportsBearing;
2308            mSupportsSpeed = supportsSpeed;
2309            mPowerRequirement = powerRequirement;
2310            mAccuracy = accuracy;
2311        }
2312
2313        @Override
2314        public void disable() {
2315            String name = getName();
2316            // We shouldn't normally need to lock, since this should only be called
2317            // by the service with the lock held, but let's be paranid.
2318            synchronized (mLocationListeners) {
2319                mEnabledProviders.remove(name);
2320                mDisabledProviders.add(name);
2321            }
2322        }
2323
2324        @Override
2325        public void enable() {
2326            String name = getName();
2327            // We shouldn't normally need to lock, since this should only be called
2328            // by the service with the lock held, but let's be paranid.
2329            synchronized (mLocationListeners) {
2330                mEnabledProviders.add(name);
2331                mDisabledProviders.remove(name);
2332            }
2333        }
2334
2335        @Override
2336        public boolean getLocation(Location l) {
2337            // We shouldn't normally need to lock, since this should only be called
2338            // by the service with the lock held, but let's be paranid.
2339            synchronized (mLocationListeners) {
2340                Location loc = mMockProviderLocation.get(getName());
2341                if (loc == null) {
2342                    return false;
2343                }
2344                l.set(loc);
2345                return true;
2346            }
2347        }
2348
2349        @Override
2350        public int getStatus(Bundle extras) {
2351            // We shouldn't normally need to lock, since this should only be called
2352            // by the service with the lock held, but let's be paranid.
2353            synchronized (mLocationListeners) {
2354                String name = getName();
2355                Integer s = mMockProviderStatus.get(name);
2356                int status = (s == null) ? AVAILABLE : s.intValue();
2357                Bundle newExtras = mMockProviderStatusExtras.get(name);
2358                if (newExtras != null) {
2359                    extras.clear();
2360                    extras.putAll(newExtras);
2361                }
2362                return status;
2363            }
2364        }
2365
2366        @Override
2367        public boolean isEnabled() {
2368            // We shouldn't normally need to lock, since this should only be called
2369            // by the service with the lock held, but let's be paranid.
2370            synchronized (mLocationListeners) {
2371                return mEnabledProviders.contains(getName());
2372            }
2373        }
2374
2375        @Override
2376        public int getAccuracy() {
2377            return mAccuracy;
2378        }
2379
2380        @Override
2381        public int getPowerRequirement() {
2382            return mPowerRequirement;
2383        }
2384
2385        @Override
2386        public boolean hasMonetaryCost() {
2387            return mHasMonetaryCost;
2388        }
2389
2390        @Override
2391        public boolean requiresCell() {
2392            return mRequiresCell;
2393        }
2394
2395        @Override
2396        public boolean requiresNetwork() {
2397            return mRequiresNetwork;
2398        }
2399
2400        @Override
2401        public boolean requiresSatellite() {
2402            return mRequiresSatellite;
2403        }
2404
2405        @Override
2406        public boolean supportsAltitude() {
2407            return mSupportsAltitude;
2408        }
2409
2410        @Override
2411        public boolean supportsBearing() {
2412            return mSupportsBearing;
2413        }
2414
2415        @Override
2416        public boolean supportsSpeed() {
2417            return mSupportsSpeed;
2418        }
2419    }
2420
2421    private void checkMockPermissionsSafe() {
2422        boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2423                Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
2424        if (!allowMocks) {
2425            throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2426        }
2427
2428        if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2429            PackageManager.PERMISSION_GRANTED) {
2430            throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2431        }
2432    }
2433
2434    public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2435        boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2436        boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2437        checkMockPermissionsSafe();
2438
2439        synchronized (mLocationListeners) {
2440            MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
2441                requiresCell, hasMonetaryCost, supportsAltitude,
2442                supportsSpeed, supportsBearing, powerRequirement, accuracy);
2443            if (LocationProviderImpl.getProvider(name) != null) {
2444                throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2445            }
2446            LocationProviderImpl.addProvider(provider);
2447            updateProvidersLocked();
2448        }
2449    }
2450
2451    public void removeTestProvider(String provider) {
2452        checkMockPermissionsSafe();
2453        synchronized (mLocationListeners) {
2454            LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
2455            if (p == null) {
2456                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2457            }
2458            LocationProviderImpl.removeProvider(p);
2459            updateProvidersLocked();
2460        }
2461    }
2462
2463    public void setTestProviderLocation(String provider, Location loc) {
2464        checkMockPermissionsSafe();
2465        synchronized (mLocationListeners) {
2466            if (LocationProviderImpl.getProvider(provider) == null) {
2467                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2468            }
2469            mMockProviderLocation.put(provider, loc);
2470        }
2471    }
2472
2473    public void clearTestProviderLocation(String provider) {
2474        checkMockPermissionsSafe();
2475        synchronized (mLocationListeners) {
2476            if (LocationProviderImpl.getProvider(provider) == null) {
2477                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2478            }
2479            mMockProviderLocation.remove(provider);
2480        }
2481    }
2482
2483    public void setTestProviderEnabled(String provider, boolean enabled) {
2484        checkMockPermissionsSafe();
2485        synchronized (mLocationListeners) {
2486            if (LocationProviderImpl.getProvider(provider) == null) {
2487                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2488            }
2489            if (enabled) {
2490                mEnabledProviders.add(provider);
2491                mDisabledProviders.remove(provider);
2492            } else {
2493                mEnabledProviders.remove(provider);
2494                mDisabledProviders.add(provider);
2495            }
2496            updateProvidersLocked();
2497        }
2498    }
2499
2500    public void clearTestProviderEnabled(String provider) {
2501        checkMockPermissionsSafe();
2502        synchronized (mLocationListeners) {
2503            if (LocationProviderImpl.getProvider(provider) == null) {
2504                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2505            }
2506            mEnabledProviders.remove(provider);
2507            mDisabledProviders.remove(provider);
2508            updateProvidersLocked();
2509        }
2510    }
2511
2512    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
2513        checkMockPermissionsSafe();
2514        synchronized (mLocationListeners) {
2515            if (LocationProviderImpl.getProvider(provider) == null) {
2516                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2517            }
2518            mMockProviderStatus.put(provider, new Integer(status));
2519            mMockProviderStatusExtras.put(provider, extras);
2520            mMockProviderStatusUpdateTime.put(provider, new Long(updateTime));
2521        }
2522    }
2523
2524    public void clearTestProviderStatus(String provider) {
2525        checkMockPermissionsSafe();
2526        synchronized (mLocationListeners) {
2527            if (LocationProviderImpl.getProvider(provider) == null) {
2528                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2529            }
2530            mMockProviderStatus.remove(provider);
2531            mMockProviderStatusExtras.remove(provider);
2532            mMockProviderStatusUpdateTime.remove(provider);
2533        }
2534    }
2535
2536    private void log(String log) {
2537        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2538            Log.d(TAG, log);
2539        }
2540    }
2541
2542    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2543        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2544                != PackageManager.PERMISSION_GRANTED) {
2545            pw.println("Permission Denial: can't dump AlarmManager from from pid="
2546                    + Binder.getCallingPid()
2547                    + ", uid=" + Binder.getCallingUid());
2548            return;
2549        }
2550
2551        synchronized (mLocationListeners) {
2552            pw.println("Current Location Manager state:");
2553            pw.println("  sProvidersLoaded=" + sProvidersLoaded);
2554            pw.println("  mGpsLocationProvider=" + mGpsLocationProvider);
2555            pw.println("  mGpsNavigating=" + mGpsNavigating);
2556            pw.println("  mNetworkLocationProvider=" + mNetworkLocationProvider);
2557            pw.println("  mNetworkLocationInterface=" + mNetworkLocationInterface);
2558            pw.println("  mCollector=" + mCollector);
2559            pw.println("  mSignalStrength=" + mSignalStrength);
2560            pw.println("  mAlarmInterval=" + mAlarmInterval
2561                    + " mScreenOn=" + mScreenOn
2562                    + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2563            pw.println("  mWakeLockGpsReceived=" + mWakeLockGpsReceived
2564                    + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
2565            pw.println("  mWifiWakeLockAcquired=" + mWifiWakeLockAcquired
2566                    + " mCellWakeLockAcquired=" + mCellWakeLockAcquired);
2567            pw.println("  Listeners:");
2568            int N = mListeners.size();
2569            for (int i=0; i<N; i++) {
2570                pw.println("    " + mListeners.get(i));
2571            }
2572            pw.println("  Location Listeners:");
2573            for (Map.Entry<Receiver, HashMap<String,UpdateRecord>> i
2574                    : mLocationListeners.entrySet()) {
2575                pw.println("    " + i.getKey() + ":");
2576                for (Map.Entry<String,UpdateRecord> j : i.getValue().entrySet()) {
2577                    pw.println("      " + j.getKey() + ":");
2578                    j.getValue().dump(pw, "        ");
2579                }
2580            }
2581            pw.println("  Last Fix Broadcasts:");
2582            for (Map.Entry<Receiver, HashMap<String,Location>> i
2583                    : mLastFixBroadcast.entrySet()) {
2584                pw.println("    " + i.getKey() + ":");
2585                for (Map.Entry<String,Location> j : i.getValue().entrySet()) {
2586                    pw.println("      " + j.getKey() + ":");
2587                    j.getValue().dump(new PrintWriterPrinter(pw), "        ");
2588                }
2589            }
2590            pw.println("  Last Status Broadcasts:");
2591            for (Map.Entry<Receiver, HashMap<String,Long>> i
2592                    : mLastStatusBroadcast.entrySet()) {
2593                pw.println("    " + i.getKey() + ":");
2594                for (Map.Entry<String,Long> j : i.getValue().entrySet()) {
2595                    pw.println("      " + j.getKey() + " -> 0x"
2596                            + Long.toHexString(j.getValue()));
2597                }
2598            }
2599            pw.println("  Records by Provider:");
2600            for (Map.Entry<String, ArrayList<UpdateRecord>> i
2601                    : mRecordsByProvider.entrySet()) {
2602                pw.println("    " + i.getKey() + ":");
2603                for (UpdateRecord j : i.getValue()) {
2604                    pw.println("      " + j + ":");
2605                    j.dump(pw, "        ");
2606                }
2607            }
2608            pw.println("  Locations by Provider:");
2609            for (Map.Entry<String, Location> i
2610                    : mLocationsByProvider.entrySet()) {
2611                pw.println("    " + i.getKey() + ":");
2612                i.getValue().dump(new PrintWriterPrinter(pw), "      ");
2613            }
2614            pw.println("  Last Known Locations:");
2615            for (Map.Entry<String, Location> i
2616                    : mLastKnownLocation.entrySet()) {
2617                pw.println("    " + i.getKey() + ":");
2618                i.getValue().dump(new PrintWriterPrinter(pw), "      ");
2619            }
2620            if (mProximityAlerts.size() > 0) {
2621                pw.println("  Proximity Alerts:");
2622                for (Map.Entry<PendingIntent, ProximityAlert> i
2623                        : mProximityAlerts.entrySet()) {
2624                    pw.println("    " + i.getKey() + ":");
2625                    i.getValue().dump(pw, "      ");
2626                }
2627            }
2628            if (mProximitiesEntered.size() > 0) {
2629                pw.println("  Proximities Entered:");
2630                for (ProximityAlert i : mProximitiesEntered) {
2631                    pw.println("    " + i + ":");
2632                    i.dump(pw, "      ");
2633                }
2634            }
2635            pw.println("  mProximityListener=" + mProximityListener);
2636            if (mEnabledProviders.size() > 0) {
2637                pw.println("  Enabled Providers:");
2638                for (String i : mEnabledProviders) {
2639                    pw.println("    " + i);
2640                }
2641
2642            }
2643            if (mDisabledProviders.size() > 0) {
2644                pw.println("  Disabled Providers:");
2645                for (String i : mDisabledProviders) {
2646                    pw.println("    " + i);
2647                }
2648
2649            }
2650            if (mMockProviders.size() > 0) {
2651                pw.println("  Mock Providers:");
2652                for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
2653                    pw.println("    " + i.getKey() + " -> " + i.getValue());
2654                }
2655            }
2656            if (mMockProviderLocation.size() > 0) {
2657                pw.println("  Mock Provider Location:");
2658                for (Map.Entry<String, Location> i : mMockProviderLocation.entrySet()) {
2659                    pw.println("    " + i.getKey() + ":");
2660                    i.getValue().dump(new PrintWriterPrinter(pw), "      ");
2661                }
2662            }
2663            if (mMockProviderStatus.size() > 0) {
2664                pw.println("  Mock Provider Status:");
2665                for (Map.Entry<String, Integer> i : mMockProviderStatus.entrySet()) {
2666                    pw.println("    " + i.getKey() + " -> 0x"
2667                            + Integer.toHexString(i.getValue()));
2668                }
2669            }
2670            if (mMockProviderStatusExtras.size() > 0) {
2671                pw.println("  Mock Provider Status Extras:");
2672                for (Map.Entry<String, Bundle> i : mMockProviderStatusExtras.entrySet()) {
2673                    pw.println("    " + i.getKey() + " -> " + i.getValue());
2674                }
2675            }
2676            if (mMockProviderStatusUpdateTime.size() > 0) {
2677                pw.println("  Mock Provider Status Update Time:");
2678                for (Map.Entry<String, Long> i : mMockProviderStatusUpdateTime.entrySet()) {
2679                    pw.println("    " + i.getKey() + " -> " + i.getValue());
2680                }
2681            }
2682            pw.println("  Reported GPS UIDs @ seq " + mReportedGpsSeq + ":");
2683            N = mReportedGpsUids.size();
2684            for (int i=0; i<N; i++)  {
2685                pw.println("    UID " + mReportedGpsUids.keyAt(i)
2686                        + " seq=" + mReportedGpsUids.valueAt(i));
2687            }
2688        }
2689    }
2690}
2691
2692