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