LocationManagerService.java revision da996f390e17e16f2dfa60e972e7ebc4f868f37e
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_INSTALL_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 setInstallCallback(InstallCallback callback) {
569        mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER);
570        Message m = Message.obtain(mLocationHandler,
571                MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback);
572        mLocationHandler.sendMessageAtFrontOfQueue(m);
573    }
574
575    public void setNetworkLocationProvider(INetworkLocationProvider provider) {
576        mNetworkLocationInterface = provider;
577        provider.addListener(getPackageNames());
578        mNetworkLocationProvider = (LocationProviderImpl)provider;
579        LocationProviderImpl.addProvider(mNetworkLocationProvider);
580        updateProviders();
581    }
582
583    public void setLocationCollector(ILocationCollector collector) {
584        mCollector = collector;
585        if (mGpsLocationProvider != null) {
586                mGpsLocationProvider.setLocationCollector(mCollector);
587        }
588    }
589
590    private WifiManager.WifiLock getWifiWakelock() {
591        if (mWifiLock == null && mWifiManager != null) {
592            mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
593            mWifiLock.setReferenceCounted(false);
594        }
595        return mWifiLock;
596    }
597
598    private boolean isAllowedBySettings(String provider) {
599        if (mEnabledProviders.contains(provider)) {
600            return true;
601        }
602        if (mDisabledProviders.contains(provider)) {
603            return false;
604        }
605        // Use system settings
606        ContentResolver resolver = mContext.getContentResolver();
607        String allowedProviders = Settings.Secure.getString(resolver,
608           Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
609
610        return ((allowedProviders != null) && (allowedProviders.contains(provider)));
611    }
612
613    private void checkPermissions(String provider) {
614        if (LocationManager.GPS_PROVIDER.equals(provider)
615            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
616                != PackageManager.PERMISSION_GRANTED)) {
617            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
618        }
619        if (LocationManager.NETWORK_PROVIDER.equals(provider)
620            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
621                != PackageManager.PERMISSION_GRANTED)
622            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
623                != PackageManager.PERMISSION_GRANTED)) {
624            throw new SecurityException(
625                "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
626        }
627    }
628
629    private boolean isAllowedProvider(String provider) {
630        if (LocationManager.GPS_PROVIDER.equals(provider)
631            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
632                != PackageManager.PERMISSION_GRANTED)) {
633            return false;
634        }
635        if (LocationManager.NETWORK_PROVIDER.equals(provider)
636            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
637                != PackageManager.PERMISSION_GRANTED)
638            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
639                != PackageManager.PERMISSION_GRANTED)) {
640            return false;
641        }
642
643        return true;
644    }
645
646    private String[] getPackageNames() {
647        // Since a single UID may correspond to multiple packages, this can only be used as an
648        // approximation for tracking
649        return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
650    }
651
652    public List<String> getAllProviders() {
653        try {
654            return _getAllProviders();
655        } catch (SecurityException se) {
656            throw se;
657        } catch (Exception e) {
658            Log.e(TAG, "getAllProviders got exception:", e);
659            return null;
660        }
661    }
662
663    private List<String> _getAllProviders() {
664        if (Config.LOGD) {
665            Log.d(TAG, "getAllProviders");
666        }
667        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
668        ArrayList<String> out = new ArrayList<String>(providers.size());
669
670        for (LocationProviderImpl p : providers) {
671            out.add(p.getName());
672        }
673        return out;
674    }
675
676    public List<String> getProviders(boolean enabledOnly) {
677        try {
678            return _getProviders(enabledOnly);
679        } catch (SecurityException se) {
680            throw se;
681        } catch (Exception e) {
682            Log.e(TAG, "getProviders gotString exception:", e);
683            return null;
684        }
685    }
686
687    private List<String> _getProviders(boolean enabledOnly) {
688        if (Config.LOGD) {
689            Log.d(TAG, "getProviders");
690        }
691        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
692        ArrayList<String> out = new ArrayList<String>();
693
694        for (LocationProviderImpl p : providers) {
695            String name = p.getName();
696            if (isAllowedProvider(name)) {
697                if (enabledOnly && !isAllowedBySettings(name)) {
698                    continue;
699                }
700                out.add(name);
701            }
702        }
703        return out;
704    }
705
706    public void updateProviders() {
707        for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
708            boolean isEnabled = p.isEnabled();
709            String name = p.getName();
710            boolean shouldBeEnabled = isAllowedBySettings(name);
711
712            // Collection is only allowed when network provider is being used
713            if (mCollector != null &&
714                    p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
715                mCollector.updateNetworkProviderStatus(shouldBeEnabled);
716            }
717
718            if (isEnabled && !shouldBeEnabled) {
719                updateProviderListeners(name, false);
720            } else if (!isEnabled && shouldBeEnabled) {
721                updateProviderListeners(name, true);
722            }
723
724        }
725    }
726
727    private void updateProviderListeners(String provider, boolean enabled) {
728        int listeners = 0;
729
730        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
731        if (p == null) {
732            return;
733        }
734
735        synchronized (mRecordsByProvider) {
736            HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
737            if (records != null) {
738                for (UpdateRecord record : records) {
739                    // Sends a notification message to the receiver
740                    try {
741                        Receiver receiver = record.mReceiver;
742                        if (receiver.isListener()) {
743                            if (enabled) {
744                                receiver.getListener().onProviderEnabled(provider);
745                            } else {
746                                receiver.getListener().onProviderDisabled(provider);
747                            }
748                        } else {
749                            PendingIntent intent = receiver.getPendingIntent();
750                            Intent providerIntent = new Intent();
751                            providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
752                            try {
753                                receiver.getPendingIntent().send(mContext, 0,
754                                     providerIntent, null, null);
755                            } catch (PendingIntent.CanceledException e) {
756                                _removeUpdates(receiver);
757                            }
758                        }
759                    } catch (RemoteException e) {
760                        // The death link will clean this up.
761                    }
762                    listeners++;
763                }
764            }
765        }
766
767        if (enabled) {
768            p.enable();
769            if (listeners > 0) {
770                p.setMinTime(getMinTime(provider));
771                p.enableLocationTracking(true);
772                updateWakelockStatus(mScreenOn);
773            }
774        } else {
775            p.enableLocationTracking(false);
776            p.disable();
777            updateWakelockStatus(mScreenOn);
778        }
779
780        if (enabled && listeners > 0) {
781            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
782            Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
783            mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
784        } else {
785            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
786        }
787    }
788
789    private long getMinTime(String provider) {
790        long minTime = Long.MAX_VALUE;
791        synchronized (mRecordsByProvider) {
792            HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
793            if (records != null) {
794                for (UpdateRecord r : records) {
795                    minTime = Math.min(minTime, r.mMinTime);
796                }
797            }
798        }
799        return minTime;
800    }
801
802    private class UpdateRecord {
803        String mProvider;
804        Receiver mReceiver;
805        long mMinTime;
806        float mMinDistance;
807        String[] mPackages;
808
809        UpdateRecord(String provider, long minTime, float minDistance,
810            Receiver receiver, String[] packages) {
811            mProvider = provider;
812            mReceiver = receiver;
813            mMinTime = minTime;
814            mMinDistance = minDistance;
815            mPackages = packages;
816
817            synchronized (mRecordsByProvider) {
818                HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
819                if (records == null) {
820                    records = new HashSet<UpdateRecord>();
821                    mRecordsByProvider.put(provider, records);
822                }
823                records.add(this);
824            }
825        }
826
827        /**
828         * Method to be called when a record will no longer be used.  Calling this multiple times
829         * must have the same effect as calling it once.
830         */
831        public void dispose() {
832            synchronized (mRecordsByProvider) {
833                HashSet<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
834                records.remove(this);
835            }
836        }
837
838        /**
839         * Calls dispose().
840         */
841        @Override protected void finalize() {
842            dispose();
843        }
844    }
845
846    public void requestLocationUpdates(String provider,
847        long minTime, float minDistance, ILocationListener listener) {
848
849        try {
850            _requestLocationUpdates(provider, minTime, minDistance,
851                new Receiver(listener));
852        } catch (SecurityException se) {
853            throw se;
854        } catch (Exception e) {
855            Log.e(TAG, "requestUpdates got exception:", e);
856        }
857    }
858
859    public void requestLocationUpdatesPI(String provider,
860            long minTime, float minDistance, PendingIntent intent) {
861        try {
862            _requestLocationUpdates(provider, minTime, minDistance,
863                    new Receiver(intent));
864        } catch (SecurityException se) {
865            throw se;
866        } catch (Exception e) {
867            Log.e(TAG, "requestUpdates got exception:", e);
868        }
869    }
870
871    private synchronized void _requestLocationUpdates(String provider,
872            long minTime, float minDistance, Receiver receiver) {
873        Object key = receiver.getKey();
874        if (Config.LOGD) {
875            Log.d(TAG, "_requestLocationUpdates: listener = " + key);
876        }
877
878        LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
879        if (impl == null) {
880            throw new IllegalArgumentException("provider=" + provider);
881        }
882
883        checkPermissions(provider);
884
885        String[] packages = getPackageNames();
886
887        // so wakelock calls will succeed
888        mCallingUid = getCallingUid();
889        long identity = Binder.clearCallingIdentity();
890        try {
891            UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages);
892            synchronized (mLocationListeners) {
893                if (mListeners.get(key) == null) {
894                    try {
895                        if (receiver.isListener()) {
896                            receiver.getListener().asBinder().linkToDeath(receiver, 0);
897                        }
898                        mListeners.put(key, receiver);
899                    } catch (RemoteException e) {
900                        return;
901                    }
902                }
903
904                HashMap<String,UpdateRecord> records = mLocationListeners.get(key);
905                if (records == null) {
906                    records = new HashMap<String,UpdateRecord>();
907                    mLocationListeners.put(key, records);
908                }
909                UpdateRecord oldRecord = records.put(provider, r);
910                if (oldRecord != null) {
911                    oldRecord.dispose();
912                }
913
914                boolean isProviderEnabled = isAllowedBySettings(provider);
915                if (isProviderEnabled) {
916                    if (provider.equals(LocationManager.GPS_PROVIDER)) {
917                        try {
918                            mBatteryStats.noteRequestGpsOn(mCallingUid);
919                        } catch (RemoteException e) {
920                            Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
921                        }
922                    }
923
924                    long minTimeForProvider = getMinTime(provider);
925                    impl.setMinTime(minTimeForProvider);
926                    impl.enableLocationTracking(true);
927                    updateWakelockStatus(mScreenOn);
928
929                    // Clear heartbeats if any before starting a new one
930                    mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
931                    Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
932                    mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
933                } else {
934                    try {
935                        // Notify the listener that updates are currently disabled
936                        if (receiver.isListener()) {
937                            receiver.getListener().onProviderDisabled(provider);
938                        }
939                    } catch(RemoteException e) {
940                        Log.w(TAG, "RemoteException calling onProviderDisabled on " +
941                                receiver.getListener());
942                    }
943                }
944            }
945        } finally {
946            Binder.restoreCallingIdentity(identity);
947            mCallingUid = -1;
948        }
949    }
950
951    public void removeUpdates(ILocationListener listener) {
952        try {
953            _removeUpdates(new Receiver(listener));
954        } catch (SecurityException se) {
955            throw se;
956        } catch (Exception e) {
957            Log.e(TAG, "removeUpdates got exception:", e);
958        }
959    }
960
961    public void removeUpdatesPI(PendingIntent intent) {
962        try {
963            _removeUpdates(new Receiver(intent));
964        } catch (SecurityException se) {
965            throw se;
966        } catch (Exception e) {
967            Log.e(TAG, "removeUpdates got exception:", e);
968        }
969    }
970
971    private synchronized void _removeUpdates(Receiver receiver) {
972        Object key = receiver.getKey();
973        if (Config.LOGD) {
974            Log.d(TAG, "_removeUpdates: listener = " + key);
975        }
976
977        // so wakelock calls will succeed
978        mCallingUid = getCallingUid();
979        long identity = Binder.clearCallingIdentity();
980        try {
981            synchronized (mLocationListeners) {
982                Receiver myReceiver = mListeners.remove(key);
983                if ((myReceiver != null) && (myReceiver.isListener())) {
984                    myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
985                }
986
987                // Record which providers were associated with this listener
988                HashSet<String> providers = new HashSet<String>();
989                HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(key);
990                if (oldRecords != null) {
991                    // Call dispose() on the obsolete update records.
992                    for (UpdateRecord record : oldRecords.values()) {
993                        if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
994                            if (mNetworkLocationInterface != null) {
995                                mNetworkLocationInterface.removeListener(record.mPackages);
996                            }
997                        }
998                        record.dispose();
999                    }
1000                    // Accumulate providers
1001                    providers.addAll(oldRecords.keySet());
1002                }
1003
1004                if (providers.contains("gps")) {
1005                    try {
1006                        mBatteryStats.noteRequestGpsOff(mCallingUid);
1007                    } catch (RemoteException e) {
1008                        Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
1009                    }
1010                }
1011
1012                mLocationListeners.remove(key);
1013                mLastFixBroadcast.remove(key);
1014                mLastStatusBroadcast.remove(key);
1015
1016                // See if the providers associated with this listener have any
1017                // other listeners; if one does, inform it of the new smallest minTime
1018                // value; if one does not, disable location tracking for it
1019                for (String provider : providers) {
1020                    // If provider is already disabled, don't need to do anything
1021                    if (!isAllowedBySettings(provider)) {
1022                        continue;
1023                    }
1024
1025                    boolean hasOtherListener = false;
1026                    synchronized (mRecordsByProvider) {
1027                        HashSet<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1028                        if (recordsForProvider != null && recordsForProvider.size() > 0) {
1029                            hasOtherListener = true;
1030                        }
1031                    }
1032
1033                    LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1034                    if (p != null) {
1035                        if (hasOtherListener) {
1036                            p.setMinTime(getMinTime(provider));
1037                        } else {
1038                            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1039                            p.enableLocationTracking(false);
1040                        }
1041                    }
1042                }
1043
1044                updateWakelockStatus(mScreenOn);
1045            }
1046        } finally {
1047            Binder.restoreCallingIdentity(identity);
1048            mCallingUid = -1;
1049        }
1050    }
1051
1052    public boolean addGpsStatusListener(IGpsStatusListener listener) {
1053        if (mGpsLocationProvider == null) {
1054            return false;
1055        }
1056        if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1057            PackageManager.PERMISSION_GRANTED) {
1058            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1059        }
1060
1061        try {
1062            mGpsLocationProvider.addGpsStatusListener(listener);
1063        } catch (RemoteException e) {
1064            Log.w(TAG, "RemoteException in addGpsStatusListener");
1065            return false;
1066        }
1067        return true;
1068    }
1069
1070    public void removeGpsStatusListener(IGpsStatusListener listener) {
1071        mGpsLocationProvider.removeGpsStatusListener(listener);
1072    }
1073
1074    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1075        // first check for permission to the provider
1076        checkPermissions(provider);
1077        // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1078        if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1079                != PackageManager.PERMISSION_GRANTED)) {
1080            throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1081        }
1082
1083        LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1084        if (provider == null) {
1085            return false;
1086        }
1087
1088        return impl.sendExtraCommand(command, extras);
1089    }
1090
1091    class ProximityAlert {
1092        double mLatitude;
1093        double mLongitude;
1094        float mRadius;
1095        long mExpiration;
1096        PendingIntent mIntent;
1097        Location mLocation;
1098
1099        public ProximityAlert(double latitude, double longitude,
1100            float radius, long expiration, PendingIntent intent) {
1101            mLatitude = latitude;
1102            mLongitude = longitude;
1103            mRadius = radius;
1104            mExpiration = expiration;
1105            mIntent = intent;
1106
1107            mLocation = new Location("");
1108            mLocation.setLatitude(latitude);
1109            mLocation.setLongitude(longitude);
1110        }
1111
1112        public long getExpiration() {
1113            return mExpiration;
1114        }
1115
1116        public PendingIntent getIntent() {
1117            return mIntent;
1118        }
1119
1120        public boolean isInProximity(double latitude, double longitude) {
1121            Location loc = new Location("");
1122            loc.setLatitude(latitude);
1123            loc.setLongitude(longitude);
1124
1125            double radius = loc.distanceTo(mLocation);
1126            return radius <= mRadius;
1127        }
1128    }
1129
1130    // Listener for receiving locations to trigger proximity alerts
1131    class ProximityListener extends ILocationListener.Stub {
1132
1133        boolean isGpsAvailable = false;
1134
1135        public void onLocationChanged(Location loc) {
1136
1137            // If Gps is available, then ignore updates from NetworkLocationProvider
1138            if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1139                isGpsAvailable = true;
1140            }
1141            if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1142                return;
1143            }
1144
1145            // Process proximity alerts
1146            long now = System.currentTimeMillis();
1147            double latitude = loc.getLatitude();
1148            double longitude = loc.getLongitude();
1149            ArrayList<PendingIntent> intentsToRemove = null;
1150
1151            for (ProximityAlert alert : mProximityAlerts.values()) {
1152                PendingIntent intent = alert.getIntent();
1153                long expiration = alert.getExpiration();
1154
1155                if ((expiration == -1) || (now <= expiration)) {
1156                    boolean entered = mProximitiesEntered.contains(alert);
1157                    boolean inProximity =
1158                        alert.isInProximity(latitude, longitude);
1159                    if (!entered && inProximity) {
1160                        if (Config.LOGD) {
1161                            Log.i(TAG, "Entered alert");
1162                        }
1163                        mProximitiesEntered.add(alert);
1164                        Intent enteredIntent = new Intent();
1165                        enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1166                        try {
1167                            intent.send(mContext, 0, enteredIntent, null, null);
1168                        } catch (PendingIntent.CanceledException e) {
1169                            if (Config.LOGD) {
1170                                Log.i(TAG, "Canceled proximity alert: " + alert, e);
1171                            }
1172                            if (intentsToRemove == null) {
1173                                intentsToRemove = new ArrayList<PendingIntent>();
1174                            }
1175                            intentsToRemove.add(intent);
1176                        }
1177                    } else if (entered && !inProximity) {
1178                        if (Config.LOGD) {
1179                            Log.i(TAG, "Exited alert");
1180                        }
1181                        mProximitiesEntered.remove(alert);
1182                        Intent exitedIntent = new Intent();
1183                        exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1184                        try {
1185                            intent.send(mContext, 0, exitedIntent, null, null);
1186                        } catch (PendingIntent.CanceledException e) {
1187                            if (Config.LOGD) {
1188                                Log.i(TAG, "Canceled proximity alert: " + alert, e);
1189                            }
1190                            if (intentsToRemove == null) {
1191                                intentsToRemove = new ArrayList<PendingIntent>();
1192                            }
1193                            intentsToRemove.add(intent);
1194                        }
1195                    }
1196                } else {
1197                    // Mark alert for expiration
1198                    if (Config.LOGD) {
1199                        Log.i(TAG, "Expiring proximity alert: " + alert);
1200                    }
1201                    if (intentsToRemove == null) {
1202                        intentsToRemove = new ArrayList<PendingIntent>();
1203                    }
1204                    intentsToRemove.add(alert.getIntent());
1205                }
1206            }
1207
1208            // Remove expired alerts
1209            if (intentsToRemove != null) {
1210                for (PendingIntent i : intentsToRemove) {
1211                    mProximityAlerts.remove(i);
1212                    ProximityAlert alert = mProximityAlerts.get(i);
1213                    mProximitiesEntered.remove(alert);
1214                }
1215            }
1216
1217        }
1218
1219        public void onProviderDisabled(String provider) {
1220            if (provider.equals(LocationManager.GPS_PROVIDER)) {
1221                isGpsAvailable = false;
1222            }
1223        }
1224
1225        public void onProviderEnabled(String provider) {
1226            // ignore
1227        }
1228
1229        public void onStatusChanged(String provider, int status, Bundle extras) {
1230            if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1231                (status != LocationProvider.AVAILABLE)) {
1232                isGpsAvailable = false;
1233            }
1234        }
1235    }
1236
1237    public void addProximityAlert(double latitude, double longitude,
1238        float radius, long expiration, PendingIntent intent) {
1239        try {
1240          _addProximityAlert(latitude, longitude, radius, expiration, intent);
1241        } catch (SecurityException se) {
1242            throw se;
1243        } catch (Exception e) {
1244            Log.e(TAG, "addProximityAlert got exception:", e);
1245        }
1246    }
1247
1248    private void _addProximityAlert(double latitude, double longitude,
1249        float radius, long expiration, PendingIntent intent) {
1250        if (Config.LOGD) {
1251            Log.d(TAG, "addProximityAlert: latitude = " + latitude +
1252                    ", longitude = " + longitude +
1253                    ", expiration = " + expiration +
1254                    ", intent = " + intent);
1255        }
1256
1257        // Require ability to access all providers for now
1258        if (!isAllowedProvider(LocationManager.GPS_PROVIDER) ||
1259            !isAllowedProvider(LocationManager.NETWORK_PROVIDER)) {
1260            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1261        }
1262
1263        if (expiration != -1) {
1264            expiration += System.currentTimeMillis();
1265        }
1266        ProximityAlert alert = new ProximityAlert(latitude, longitude, radius, expiration, intent);
1267        mProximityAlerts.put(intent, alert);
1268
1269        if (mProximityListener == null) {
1270            mProximityListener = new Receiver(new ProximityListener());
1271
1272            LocationProvider provider = LocationProviderImpl.getProvider(
1273                LocationManager.GPS_PROVIDER);
1274            if (provider != null) {
1275                _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1276            }
1277
1278            provider =
1279                LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1280            if (provider != null) {
1281                _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1282            }
1283        }
1284    }
1285
1286    public void removeProximityAlert(PendingIntent intent) {
1287        try {
1288           _removeProximityAlert(intent);
1289        } catch (SecurityException se) {
1290            throw se;
1291        } catch (Exception e) {
1292            Log.e(TAG, "removeProximityAlert got exception:", e);
1293        }
1294    }
1295
1296    private void _removeProximityAlert(PendingIntent intent) {
1297        if (Config.LOGD) {
1298            Log.d(TAG, "removeProximityAlert: intent = " + intent);
1299        }
1300
1301        mProximityAlerts.remove(intent);
1302        if (mProximityAlerts.size() == 0) {
1303            _removeUpdates(mProximityListener);
1304            mProximityListener = null;
1305        }
1306     }
1307
1308    /**
1309     * @return null if the provider does not exits
1310     * @throw SecurityException if the provider is not allowed to be
1311     * accessed by the caller
1312     */
1313    public Bundle getProviderInfo(String provider) {
1314        try {
1315            return _getProviderInfo(provider);
1316        } catch (SecurityException se) {
1317            throw se;
1318        } catch (Exception e) {
1319            Log.e(TAG, "_getProviderInfo got exception:", e);
1320            return null;
1321        }
1322    }
1323
1324    private Bundle _getProviderInfo(String provider) {
1325        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1326        if (p == null) {
1327            return null;
1328        }
1329
1330        checkPermissions(provider);
1331
1332        Bundle b = new Bundle();
1333        b.putBoolean("network", p.requiresNetwork());
1334        b.putBoolean("satellite", p.requiresSatellite());
1335        b.putBoolean("cell", p.requiresCell());
1336        b.putBoolean("cost", p.hasMonetaryCost());
1337        b.putBoolean("altitude", p.supportsAltitude());
1338        b.putBoolean("speed", p.supportsSpeed());
1339        b.putBoolean("bearing", p.supportsBearing());
1340        b.putInt("power", p.getPowerRequirement());
1341        b.putInt("accuracy", p.getAccuracy());
1342
1343        return b;
1344    }
1345
1346    public boolean isProviderEnabled(String provider) {
1347        try {
1348            return _isProviderEnabled(provider);
1349        } catch (SecurityException se) {
1350            throw se;
1351        } catch (Exception e) {
1352            Log.e(TAG, "isProviderEnabled got exception:", e);
1353            return false;
1354        }
1355    }
1356
1357    private boolean _isProviderEnabled(String provider) {
1358        checkPermissions(provider);
1359
1360        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1361        if (p == null) {
1362            throw new IllegalArgumentException("provider=" + provider);
1363        }
1364        return isAllowedBySettings(provider);
1365    }
1366
1367    public Location getLastKnownLocation(String provider) {
1368        try {
1369            return _getLastKnownLocation(provider);
1370        } catch (SecurityException se) {
1371            throw se;
1372        } catch (Exception e) {
1373            Log.e(TAG, "getLastKnownLocation got exception:", e);
1374            return null;
1375        }
1376    }
1377
1378    private Location _getLastKnownLocation(String provider) {
1379        checkPermissions(provider);
1380
1381        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1382        if (p == null) {
1383            throw new IllegalArgumentException("provider=" + provider);
1384        }
1385
1386        if (!isAllowedBySettings(provider)) {
1387            return null;
1388        }
1389
1390        Location location = mLastKnownLocation.get(provider);
1391        if (location == null) {
1392            // Get the persistent last known location for the provider
1393            location = readLastKnownLocation(provider);
1394            if (location != null) {
1395                mLastKnownLocation.put(provider, location);
1396            }
1397        }
1398
1399        return location;
1400    }
1401
1402    private boolean shouldBroadcast(Location loc, Location lastLoc, UpdateRecord record) {
1403        // Always broadcast the first update
1404        if (lastLoc == null) {
1405            return true;
1406        }
1407
1408        // Don't broadcast same location again regardless of condition
1409        // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1410        if (loc.getTime() == lastLoc.getTime()) {
1411            return false;
1412        }
1413
1414        // Check whether sufficient distance has been traveled
1415        double minDistance = record.mMinDistance;
1416        if (minDistance > 0.0) {
1417            if (loc.distanceTo(lastLoc) <= minDistance) {
1418                return false;
1419            }
1420        }
1421
1422        return true;
1423    }
1424
1425    private void handleLocationChanged(String provider) {
1426        HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1427        if (records == null || records.size() == 0) {
1428            return;
1429        }
1430
1431        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1432        if (p == null) {
1433            return;
1434        }
1435
1436        // Get location object
1437        Location loc = mLocationsByProvider.get(provider);
1438        if (loc == null) {
1439            loc = new Location(provider);
1440            mLocationsByProvider.put(provider, loc);
1441        } else {
1442            loc.reset();
1443        }
1444
1445        // Use the mock location if available
1446        Location mockLoc = mMockProviderLocation.get(provider);
1447        boolean locationValid;
1448        if (mockLoc != null) {
1449            locationValid = true;
1450            loc.set(mockLoc);
1451        } else {
1452            locationValid = p.getLocation(loc);
1453        }
1454
1455        // Update last known location for provider
1456        if (locationValid) {
1457            Location location = mLastKnownLocation.get(provider);
1458            if (location == null) {
1459                mLastKnownLocation.put(provider, new Location(loc));
1460            } else {
1461                location.set(loc);
1462            }
1463            writeLastKnownLocation(provider, loc);
1464
1465            if (p instanceof INetworkLocationProvider) {
1466                mWakeLockNetworkReceived = true;
1467            } else if (p instanceof GpsLocationProvider) {
1468                // Gps location received signal is in NetworkStateBroadcastReceiver
1469            }
1470        }
1471
1472        // Fetch latest status update time
1473        long newStatusUpdateTime = p.getStatusUpdateTime();
1474
1475        // Override real time with mock time if present
1476        Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider);
1477        if (mockStatusUpdateTime != null) {
1478            newStatusUpdateTime = mockStatusUpdateTime.longValue();
1479        }
1480
1481        // Get latest status
1482        Bundle extras = new Bundle();
1483        int status = p.getStatus(extras);
1484
1485        // Override status with mock status if present
1486        Integer mockStatus = mMockProviderStatus.get(provider);
1487        if (mockStatus != null) {
1488            status = mockStatus.intValue();
1489        }
1490
1491        // Override extras with mock extras if present
1492        Bundle mockExtras = mMockProviderStatusExtras.get(provider);
1493        if (mockExtras != null) {
1494            extras.clear();
1495            extras.putAll(mockExtras);
1496        }
1497
1498        // Broadcast location or status to all listeners
1499        for (UpdateRecord r : records) {
1500            Receiver receiver = r.mReceiver;
1501            Object key = receiver.getKey();
1502
1503            // Broadcast location only if it is valid
1504            if (locationValid) {
1505                HashMap<String,Location> map = mLastFixBroadcast.get(key);
1506                if (map == null) {
1507                    map = new HashMap<String,Location>();
1508                    mLastFixBroadcast.put(key, map);
1509                }
1510                Location lastLoc = map.get(provider);
1511                if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) {
1512                    if (lastLoc == null) {
1513                        lastLoc = new Location(loc);
1514                        map.put(provider, lastLoc);
1515                    } else {
1516                        lastLoc.set(loc);
1517                    }
1518                    try {
1519                        receiver.onLocationChanged(loc);
1520                    } catch (RemoteException doe) {
1521                        Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1522                        _removeUpdates(receiver);
1523                    }
1524                }
1525            }
1526
1527            // Broadcast status message
1528            HashMap<String,Long> statusMap = mLastStatusBroadcast.get(key);
1529            if (statusMap == null) {
1530                statusMap = new HashMap<String,Long>();
1531                mLastStatusBroadcast.put(key, statusMap);
1532            }
1533            long prevStatusUpdateTime =
1534                (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1535
1536            if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1537                (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1538
1539                statusMap.put(provider, newStatusUpdateTime);
1540                try {
1541                    receiver.onStatusChanged(provider, status, extras);
1542                } catch (RemoteException doe) {
1543                    Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1544                    _removeUpdates(receiver);
1545                }
1546            }
1547        }
1548    }
1549
1550    private class LocationWorkerHandler extends Handler {
1551
1552        @Override
1553        public void handleMessage(Message msg) {
1554            try {
1555                if (msg.what == MESSAGE_HEARTBEAT) {
1556                    // log("LocationWorkerHandler: Heartbeat!");
1557
1558                    synchronized (mRecordsByProvider) {
1559                        String provider = (String) msg.obj;
1560                        if (!isAllowedBySettings(provider)) {
1561                            return;
1562                        }
1563
1564                        // Process the location fix if the screen is on or we're holding a wakelock
1565                        if (mScreenOn || (mWakeLockAcquireTime != 0)) {
1566                            handleLocationChanged(provider);
1567                        }
1568
1569                        // If it continues to have listeners
1570                        HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1571                        if (records != null && records.size() > 0) {
1572                            Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
1573                            sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1574                        }
1575                    }
1576
1577                    if ((mWakeLockAcquireTime != 0) &&
1578                        (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1579                            > MAX_TIME_FOR_WAKE_LOCK)) {
1580
1581                        removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1582                        removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1583
1584                        log("LocationWorkerHandler: Exceeded max time for wake lock");
1585                        Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1586                        sendMessageAtFrontOfQueue(m);
1587
1588                    } else if (mWakeLockAcquireTime != 0 &&
1589                        mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1590
1591                        removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1592                        removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1593
1594                        log("LocationWorkerHandler: Locations received.");
1595                        mWakeLockAcquireTime = 0;
1596                        Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1597                        sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1598                    }
1599
1600                } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1601                    log("LocationWorkerHandler: Acquire");
1602                    acquireWakeLock();
1603                } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1604                    log("LocationWorkerHandler: Release");
1605
1606                    // Update wakelock status so the next alarm is set before releasing wakelock
1607                    updateWakelockStatus(mScreenOn);
1608                    releaseWakeLock();
1609                } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) {
1610                    synchronized (LocationManagerService.class) {
1611                        Log.d(TAG, "installing network location provider");
1612                        INetworkLocationManager.InstallCallback callback =
1613                                (INetworkLocationManager.InstallCallback)msg.obj;
1614                        callback.installNetworkLocationProvider(mContext, LocationManagerService.this);
1615                    }
1616                }
1617            } catch (Exception e) {
1618                // Log, don't crash!
1619                Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1620            }
1621        }
1622    }
1623
1624    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1625
1626        private CellState mLastCellState = null;
1627        @Override
1628        public void onCellLocationChanged(CellLocation cellLocation) {
1629            try {
1630                int asu = mSignalStrength;
1631
1632                // Gets cell state
1633                mLastCellState = new CellState(mTelephonyManager, cellLocation, asu);
1634
1635                // Notify collector
1636                if (mCollector != null) {
1637                    mCollector.updateCellState(mLastCellState);
1638                }
1639
1640                // Updates providers
1641                List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1642                for (LocationProviderImpl provider : providers) {
1643                    if (provider.requiresCell()) {
1644                        provider.updateCellState(mLastCellState);
1645                    }
1646                }
1647            } catch (Exception e) {
1648                Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e);
1649            }
1650        }
1651
1652        @Override
1653        public void onSignalStrengthChanged(int asu) {
1654            mSignalStrength = asu;
1655
1656            if (mLastCellState != null) {
1657                mLastCellState.updateSignalStrength(asu);
1658            }
1659        }
1660
1661        @Override
1662        public void onDataConnectionStateChanged(int state) {
1663            if (mLastCellState != null) {
1664                mLastCellState.updateRadioType(mTelephonyManager);
1665            }
1666        }
1667    };
1668
1669    private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1670        @Override public void onReceive(Context context, Intent intent) {
1671            String action = intent.getAction();
1672
1673            if (action.equals(ALARM_INTENT)) {
1674                mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1675                mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1676
1677                log("PowerStateBroadcastReceiver: Alarm received");
1678                Message m = mLocationHandler.obtainMessage(MESSAGE_ACQUIRE_WAKE_LOCK);
1679                mLocationHandler.sendMessageAtFrontOfQueue(m);
1680
1681            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1682                log("PowerStateBroadcastReceiver: Screen off");
1683                updateWakelockStatus(false);
1684
1685            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1686                log("PowerStateBroadcastReceiver: Screen on");
1687                updateWakelockStatus(true);
1688
1689            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1690                log("PowerStateBroadcastReceiver: Battery changed");
1691                int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
1692                int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
1693                boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
1694
1695                // Notify collector battery state
1696                if (mCollector != null) {
1697                    mCollector.updateBatteryState(scale, level, plugged);
1698                }
1699            }
1700        }
1701    }
1702
1703    private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1704        @Override public void onReceive(Context context, Intent intent) {
1705            String action = intent.getAction();
1706
1707            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
1708
1709                List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
1710
1711                if (wifiScanResults == null) {
1712                    return;
1713                }
1714
1715                // Notify provider and collector of Wifi scan results
1716                if (mCollector != null) {
1717                    mCollector.updateWifiScanResults(wifiScanResults);
1718                }
1719                if (mNetworkLocationInterface != null) {
1720                    mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
1721                }
1722
1723            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1724                int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
1725
1726                boolean noConnectivity =
1727                    intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1728                if (!noConnectivity) {
1729                    networkState = LocationProvider.AVAILABLE;
1730                }
1731
1732                // Notify location providers of current network state
1733                List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1734                for (LocationProviderImpl provider : providers) {
1735                    if (provider.requiresNetwork()) {
1736                        provider.updateNetworkState(networkState);
1737                    }
1738                }
1739
1740            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1741                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1742                    WifiManager.WIFI_STATE_UNKNOWN);
1743
1744                boolean enabled;
1745                if (state == WifiManager.WIFI_STATE_ENABLED) {
1746                    enabled = true;
1747                } else if (state == WifiManager.WIFI_STATE_DISABLED) {
1748                    enabled = false;
1749                } else {
1750                    return;
1751                }
1752
1753                // Notify network provider of current wifi enabled state
1754                if (mNetworkLocationInterface != null) {
1755                    mNetworkLocationInterface.updateWifiEnabledState(enabled);
1756                }
1757
1758            } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1759
1760                final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1761                    false);
1762
1763                if (!enabled) {
1764                    // When GPS is disabled, we are OK to release wake-lock
1765                    mWakeLockGpsReceived = true;
1766                }
1767            }
1768
1769        }
1770    }
1771
1772    // Wake locks
1773
1774    private void updateWakelockStatus(boolean screenOn) {
1775        log("updateWakelockStatus(): " + screenOn);
1776
1777        boolean needsLock = false;
1778        long minTime = Integer.MAX_VALUE;
1779
1780        if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1781            needsLock = true;
1782            minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1783        }
1784
1785        if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1786            needsLock = true;
1787            minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1788            if (screenOn) {
1789                startGps();
1790            } else if (mScreenOn && !screenOn) {
1791
1792                // We just turned the screen off so stop navigating
1793                stopGps();
1794            }
1795        }
1796
1797        mScreenOn = screenOn;
1798
1799        PendingIntent sender =
1800            PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1801
1802        // Cancel existing alarm
1803        log("Cancelling existing alarm");
1804        mAlarmManager.cancel(sender);
1805
1806        if (needsLock && !mScreenOn) {
1807            long now = SystemClock.elapsedRealtime();
1808            mAlarmManager.set(
1809                AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1810            mAlarmInterval = minTime;
1811            log("Creating a new wakelock alarm with minTime = " + minTime);
1812        } else {
1813            log("No need for alarm");
1814            mAlarmInterval = -1;
1815
1816            // Clear out existing wakelocks
1817            mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1818            mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1819            releaseWakeLock();
1820        }
1821    }
1822
1823    private void acquireWakeLock() {
1824        try {
1825            acquireWakeLockX();
1826        } catch (Exception e) {
1827            // This is to catch a runtime exception thrown when we try to release an
1828            // already released lock.
1829            Log.e(TAG, "exception in acquireWakeLock()", e);
1830        }
1831    }
1832
1833    private void acquireWakeLockX() {
1834        if (mWakeLock.isHeld()) {
1835            log("Must release wakelock before acquiring");
1836            mWakeLockAcquireTime = 0;
1837            mWakeLock.release();
1838        }
1839
1840        boolean networkActive = (mNetworkLocationProvider != null)
1841                && mNetworkLocationProvider.isLocationTracking();
1842        boolean gpsActive = (mGpsLocationProvider != null)
1843                && mGpsLocationProvider.isLocationTracking();
1844
1845        boolean needsLock = networkActive || gpsActive;
1846        if (!needsLock) {
1847            log("No need for Lock!");
1848            return;
1849        }
1850
1851        mWakeLockGpsReceived = !gpsActive;
1852        mWakeLockNetworkReceived = !networkActive;
1853
1854        // Acquire wake lock
1855        mWakeLock.acquire();
1856        mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1857        log("Acquired wakelock");
1858
1859        // Start the gps provider
1860        startGps();
1861
1862        // Acquire cell lock
1863        if (mCellWakeLockAcquired) {
1864            // Lock is already acquired
1865        } else if (!mWakeLockNetworkReceived) {
1866            mTelephonyManager.enableLocationUpdates();
1867            mCellWakeLockAcquired = true;
1868        } else {
1869            mCellWakeLockAcquired = false;
1870        }
1871
1872        // Notify NetworkLocationProvider
1873        if (mNetworkLocationInterface != null) {
1874            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
1875        }
1876
1877        // Acquire wifi lock
1878        WifiManager.WifiLock wifiLock = getWifiWakelock();
1879        if (wifiLock != null) {
1880            if (mWifiWakeLockAcquired) {
1881                // Lock is already acquired
1882            } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
1883                wifiLock.acquire();
1884                mWifiWakeLockAcquired = true;
1885            } else {
1886                mWifiWakeLockAcquired = false;
1887                Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
1888            }
1889        }
1890    }
1891
1892    private void startGps() {
1893        boolean gpsActive = (mGpsLocationProvider != null)
1894                    && mGpsLocationProvider.isLocationTracking();
1895        if (gpsActive) {
1896            mGpsLocationProvider.startNavigating();
1897            long identity = Binder.clearCallingIdentity();
1898            try {
1899                mBatteryStats.noteStartGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
1900            } catch (RemoteException e) {
1901                Log.w(TAG, "RemoteException calling noteStartGps on BatteryStatsService", e);
1902            } finally {
1903                Binder.restoreCallingIdentity(identity);
1904            }
1905        }
1906    }
1907
1908    private void stopGps() {
1909        boolean gpsActive = mGpsLocationProvider != null
1910                    && mGpsLocationProvider.isLocationTracking();
1911        if (gpsActive) {
1912            mGpsLocationProvider.stopNavigating();
1913            long identity = Binder.clearCallingIdentity();
1914            try {
1915                mBatteryStats.noteStopGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
1916            } catch (RemoteException e) {
1917                Log.w(TAG, "RemoteException calling noteStopGps on BatteryStatsService", e);
1918            } finally {
1919                Binder.restoreCallingIdentity(identity);
1920            }
1921        }
1922    }
1923
1924    private void releaseWakeLock() {
1925        try {
1926            releaseWakeLockX();
1927        } catch (Exception e) {
1928            // This is to catch a runtime exception thrown when we try to release an
1929            // already released lock.
1930            Log.e(TAG, "exception in releaseWakeLock()", e);
1931        }
1932    }
1933
1934    private void releaseWakeLockX() {
1935        // Release wifi lock
1936        WifiManager.WifiLock wifiLock = getWifiWakelock();
1937        if (wifiLock != null) {
1938            if (mWifiWakeLockAcquired) {
1939                wifiLock.release();
1940                mWifiWakeLockAcquired = false;
1941            }
1942        }
1943
1944        if (!mScreenOn) {
1945            // Stop the gps
1946            stopGps();
1947        }
1948
1949        // Release cell lock
1950        if (mCellWakeLockAcquired) {
1951            mTelephonyManager.disableLocationUpdates();
1952            mCellWakeLockAcquired = false;
1953        }
1954
1955        // Notify NetworkLocationProvider
1956        if (mNetworkLocationInterface != null) {
1957            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
1958        }
1959
1960        // Release wake lock
1961        mWakeLockAcquireTime = 0;
1962        if (mWakeLock.isHeld()) {
1963            log("Released wakelock");
1964            mWakeLock.release();
1965        } else {
1966            log("Can't release wakelock again!");
1967        }
1968    }
1969
1970    // Geocoder
1971
1972    public String getFromLocation(double latitude, double longitude, int maxResults,
1973        String language, String country, String variant, String appName, List<Address> addrs) {
1974        if (mNetworkLocationInterface != null) {
1975            return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults,
1976                    language, country, variant, appName, addrs);
1977        } else {
1978            return null;
1979        }
1980    }
1981
1982    public String getFromLocationName(String locationName,
1983        double lowerLeftLatitude, double lowerLeftLongitude,
1984        double upperRightLatitude, double upperRightLongitude, int maxResults,
1985        String language, String country, String variant, String appName, List<Address> addrs) {
1986        if (mNetworkLocationInterface != null) {
1987            return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude,
1988                    lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
1989                    language, country, variant, appName, addrs);
1990        } else {
1991            return null;
1992        }
1993    }
1994
1995    // Mock Providers
1996
1997    class MockProvider extends LocationProviderImpl {
1998        boolean mRequiresNetwork;
1999        boolean mRequiresSatellite;
2000        boolean mRequiresCell;
2001        boolean mHasMonetaryCost;
2002        boolean mSupportsAltitude;
2003        boolean mSupportsSpeed;
2004        boolean mSupportsBearing;
2005        int mPowerRequirement;
2006        int mAccuracy;
2007
2008        public MockProvider(String name,  boolean requiresNetwork, boolean requiresSatellite,
2009            boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2010            boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2011            super(name);
2012
2013            mRequiresNetwork = requiresNetwork;
2014            mRequiresSatellite = requiresSatellite;
2015            mRequiresCell = requiresCell;
2016            mHasMonetaryCost = hasMonetaryCost;
2017            mSupportsAltitude = supportsAltitude;
2018            mSupportsBearing = supportsBearing;
2019            mSupportsSpeed = supportsSpeed;
2020            mPowerRequirement = powerRequirement;
2021            mAccuracy = accuracy;
2022        }
2023
2024        @Override
2025        public void disable() {
2026            String name = getName();
2027            mEnabledProviders.remove(name);
2028            mDisabledProviders.add(name);
2029        }
2030
2031        @Override
2032        public void enable() {
2033            String name = getName();
2034            mEnabledProviders.add(name);
2035            mDisabledProviders.remove(name);
2036        }
2037
2038        @Override
2039        public boolean getLocation(Location l) {
2040            Location loc = mMockProviderLocation.get(getName());
2041            if (loc == null) {
2042                return false;
2043            }
2044            l.set(loc);
2045            return true;
2046        }
2047
2048        @Override
2049        public int getStatus(Bundle extras) {
2050            String name = getName();
2051            Integer s = mMockProviderStatus.get(name);
2052            int status = (s == null) ? AVAILABLE : s.intValue();
2053            Bundle newExtras = mMockProviderStatusExtras.get(name);
2054            if (newExtras != null) {
2055                extras.clear();
2056                extras.putAll(newExtras);
2057            }
2058            return status;
2059        }
2060
2061        @Override
2062        public boolean isEnabled() {
2063            return mEnabledProviders.contains(getName());
2064        }
2065
2066        @Override
2067        public int getAccuracy() {
2068            return mAccuracy;
2069        }
2070
2071        @Override
2072        public int getPowerRequirement() {
2073            return mPowerRequirement;
2074        }
2075
2076        @Override
2077        public boolean hasMonetaryCost() {
2078            return mHasMonetaryCost;
2079        }
2080
2081        @Override
2082        public boolean requiresCell() {
2083            return mRequiresCell;
2084        }
2085
2086        @Override
2087        public boolean requiresNetwork() {
2088            return mRequiresNetwork;
2089        }
2090
2091        @Override
2092        public boolean requiresSatellite() {
2093            return mRequiresSatellite;
2094        }
2095
2096        @Override
2097        public boolean supportsAltitude() {
2098            return mSupportsAltitude;
2099        }
2100
2101        @Override
2102        public boolean supportsBearing() {
2103            return mSupportsBearing;
2104        }
2105
2106        @Override
2107        public boolean supportsSpeed() {
2108            return mSupportsSpeed;
2109        }
2110    }
2111
2112    private void checkMockPermissions() {
2113        boolean allowMocks = false;
2114        try {
2115            allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2116                Settings.Secure.ALLOW_MOCK_LOCATION) == 1;
2117        } catch (SettingNotFoundException e) {
2118            // Do nothing
2119        }
2120        if (!allowMocks) {
2121            throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2122        }
2123
2124        if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2125            PackageManager.PERMISSION_GRANTED) {
2126            throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2127        }
2128    }
2129
2130    public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2131        boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2132        boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2133        checkMockPermissions();
2134
2135        MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
2136            requiresCell, hasMonetaryCost, supportsAltitude,
2137            supportsSpeed, supportsBearing, powerRequirement, accuracy);
2138        if (LocationProviderImpl.getProvider(name) != null) {
2139            throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2140        }
2141        LocationProviderImpl.addProvider(provider);
2142        updateProviders();
2143    }
2144
2145    public void removeTestProvider(String provider) {
2146        checkMockPermissions();
2147        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
2148        if (p == null) {
2149            throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2150        }
2151        LocationProviderImpl.removeProvider(p);
2152        updateProviders();
2153    }
2154
2155    public void setTestProviderLocation(String provider, Location loc) {
2156        checkMockPermissions();
2157        if (LocationProviderImpl.getProvider(provider) == null) {
2158            throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2159        }
2160        mMockProviderLocation.put(provider, loc);
2161    }
2162
2163    public void clearTestProviderLocation(String provider) {
2164        checkMockPermissions();
2165        if (LocationProviderImpl.getProvider(provider) == null) {
2166            throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2167        }
2168        mMockProviderLocation.remove(provider);
2169    }
2170
2171    public void setTestProviderEnabled(String provider, boolean enabled) {
2172        checkMockPermissions();
2173        if (LocationProviderImpl.getProvider(provider) == null) {
2174            throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2175        }
2176        if (enabled) {
2177            mEnabledProviders.add(provider);
2178            mDisabledProviders.remove(provider);
2179        } else {
2180            mEnabledProviders.remove(provider);
2181            mDisabledProviders.add(provider);
2182        }
2183        updateProviders();
2184    }
2185
2186    public void clearTestProviderEnabled(String provider) {
2187        checkMockPermissions();
2188        if (LocationProviderImpl.getProvider(provider) == null) {
2189            throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2190        }
2191        mEnabledProviders.remove(provider);
2192        mDisabledProviders.remove(provider);
2193        updateProviders();
2194    }
2195
2196    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
2197        checkMockPermissions();
2198        if (LocationProviderImpl.getProvider(provider) == null) {
2199            throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2200        }
2201        mMockProviderStatus.put(provider, new Integer(status));
2202        mMockProviderStatusExtras.put(provider, extras);
2203        mMockProviderStatusUpdateTime.put(provider, new Long(updateTime));
2204    }
2205
2206    public void clearTestProviderStatus(String provider) {
2207        checkMockPermissions();
2208        if (LocationProviderImpl.getProvider(provider) == null) {
2209            throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2210        }
2211        mMockProviderStatus.remove(provider);
2212        mMockProviderStatusExtras.remove(provider);
2213        mMockProviderStatusUpdateTime.remove(provider);
2214    }
2215
2216    private void log(String log) {
2217        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2218            Log.d(TAG, log);
2219        }
2220    }
2221}
2222
2223