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