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