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