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