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