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