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