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