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