GpsLocationProvider.java revision 29c84340a14e70419449628ab193d11b7a609e9a
1/*
2 * Copyright (C) 2008 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.internal.location;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.location.Criteria;
26import android.location.IGpsStatusListener;
27import android.location.IGpsStatusProvider;
28import android.location.ILocationManager;
29import android.location.ILocationProvider;
30import android.location.Location;
31import android.location.LocationManager;
32import android.location.LocationProvider;
33import android.net.ConnectivityManager;
34import android.net.SntpClient;
35import android.os.Bundle;
36import android.os.IBinder;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.SystemClock;
40import android.util.Config;
41import android.util.Log;
42import android.util.SparseIntArray;
43
44import com.android.internal.app.IBatteryStats;
45import com.android.internal.telephony.Phone;
46import com.android.internal.telephony.TelephonyIntents;
47
48import java.io.File;
49import java.io.FileInputStream;
50import java.io.IOException;
51import java.net.InetAddress;
52import java.net.UnknownHostException;
53import java.util.ArrayList;
54import java.util.Properties;
55
56/**
57 * A GPS implementation of LocationProvider used by LocationManager.
58 *
59 * {@hide}
60 */
61public class GpsLocationProvider extends ILocationProvider.Stub {
62
63    private static final String TAG = "GpsLocationProvider";
64
65    private static final boolean DEBUG = true;
66    private static final boolean VERBOSE = false;
67
68    /**
69     * Broadcast intent action indicating that the GPS has either been
70     * enabled or disabled. An intent extra provides this state as a boolean,
71     * where {@code true} means enabled.
72     * @see #EXTRA_ENABLED
73     *
74     * {@hide}
75     */
76    public static final String GPS_ENABLED_CHANGE_ACTION =
77        "android.location.GPS_ENABLED_CHANGE";
78
79    /**
80     * Broadcast intent action indicating that the GPS has either started or
81     * stopped receiving GPS fixes. An intent extra provides this state as a
82     * boolean, where {@code true} means that the GPS is actively receiving fixes.
83     * @see #EXTRA_ENABLED
84     *
85     * {@hide}
86     */
87    public static final String GPS_FIX_CHANGE_ACTION =
88        "android.location.GPS_FIX_CHANGE";
89
90    /**
91     * The lookup key for a boolean that indicates whether GPS is enabled or
92     * disabled. {@code true} means GPS is enabled. Retrieve it with
93     * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
94     *
95     * {@hide}
96     */
97    public static final String EXTRA_ENABLED = "enabled";
98
99    // these need to match GpsPositionMode enum in gps.h
100    private static final int GPS_POSITION_MODE_STANDALONE = 0;
101    private static final int GPS_POSITION_MODE_MS_BASED = 1;
102    private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
103
104    // these need to match GpsStatusValue defines in gps.h
105    private static final int GPS_STATUS_NONE = 0;
106    private static final int GPS_STATUS_SESSION_BEGIN = 1;
107    private static final int GPS_STATUS_SESSION_END = 2;
108    private static final int GPS_STATUS_ENGINE_ON = 3;
109    private static final int GPS_STATUS_ENGINE_OFF = 4;
110
111    // these need to match GpsSuplStatusValue defines in gps.h
112    /** SUPL status event values. */
113    private static final int GPS_REQUEST_SUPL_DATA_CONN = 1;
114    private static final int GPS_RELEASE_SUPL_DATA_CONN = 2;
115    private static final int GPS_SUPL_DATA_CONNECTED = 3;
116    private static final int GPS_SUPL_DATA_CONN_DONE = 4;
117    private static final int GPS_SUPL_DATA_CONN_FAILED = 5;
118
119    // these need to match GpsLocationFlags enum in gps.h
120    private static final int LOCATION_INVALID = 0;
121    private static final int LOCATION_HAS_LAT_LONG = 1;
122    private static final int LOCATION_HAS_ALTITUDE = 2;
123    private static final int LOCATION_HAS_SPEED = 4;
124    private static final int LOCATION_HAS_BEARING = 8;
125    private static final int LOCATION_HAS_ACCURACY = 16;
126
127// IMPORTANT - the GPS_DELETE_* symbols here must match constants in GpsLocationProvider.java
128    private static final int GPS_DELETE_EPHEMERIS = 0x0001;
129    private static final int GPS_DELETE_ALMANAC = 0x0002;
130    private static final int GPS_DELETE_POSITION = 0x0004;
131    private static final int GPS_DELETE_TIME = 0x0008;
132    private static final int GPS_DELETE_IONO = 0x0010;
133    private static final int GPS_DELETE_UTC = 0x0020;
134    private static final int GPS_DELETE_HEALTH = 0x0040;
135    private static final int GPS_DELETE_SVDIR = 0x0080;
136    private static final int GPS_DELETE_SVSTEER = 0x0100;
137    private static final int GPS_DELETE_SADATA = 0x0200;
138    private static final int GPS_DELETE_RTI = 0x0400;
139    private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
140    private static final int GPS_DELETE_ALL = 0xFFFF;
141
142    // for mSuplDataConnectionState
143    private static final int SUPL_DATA_CONNECTION_CLOSED = 0;
144    private static final int SUPL_DATA_CONNECTION_OPENING = 1;
145    private static final int SUPL_DATA_CONNECTION_OPEN = 2;
146
147    private static final String PROPERTIES_FILE = "/etc/gps.conf";
148
149    private int mLocationFlags = LOCATION_INVALID;
150
151    // current status
152    private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
153
154    // time for last status update
155    private long mStatusUpdateTime = SystemClock.elapsedRealtime();
156
157    // turn off GPS fix icon if we haven't received a fix in 10 seconds
158    private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
159
160    // number of fixes to receive before disabling GPS
161    private static final int MIN_FIX_COUNT = 10;
162
163    // true if we are enabled
164    private boolean mEnabled;
165
166    // true if we have network connectivity
167    private boolean mNetworkAvailable;
168
169    // true if GPS is navigating
170    private boolean mNavigating;
171
172    // requested frequency of fixes, in seconds
173    private int mFixInterval = 1;
174
175    // number of fixes we have received since we started navigating
176    private int mFixCount;
177
178    private int mPositionMode = GPS_POSITION_MODE_STANDALONE;
179
180    // true if we started navigation
181    private boolean mStarted;
182
183    // for calculating time to first fix
184    private long mFixRequestTime = 0;
185    // time to first fix for most recent session
186    private int mTTFF = 0;
187    // time we received our last fix
188    private long mLastFixTime;
189
190    // properties loaded from PROPERTIES_FILE
191    private Properties mProperties;
192    private String mNtpServer;
193
194    private final Context mContext;
195    private final ILocationManager mLocationManager;
196    private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
197    private Bundle mLocationExtras = new Bundle();
198    private ArrayList<Listener> mListeners = new ArrayList<Listener>();
199    private GpsEventThread mEventThread;
200    private GpsNetworkThread mNetworkThread;
201    private Object mNetworkThreadLock = new Object();
202
203    private String mSuplHost;
204    private int mSuplPort;
205    private boolean mSetSuplServer;
206    private String mSuplApn;
207    private int mSuplDataConnectionState;
208    private final ConnectivityManager mConnMgr;
209
210    // Alarms
211    private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
212    private final AlarmManager mAlarmManager;
213    private final PendingIntent mWakeupIntent;
214
215    private final IBatteryStats mBatteryStats;
216    private final SparseIntArray mClientUids = new SparseIntArray();
217
218    // how often to request NTP time, in milliseconds
219    // current setting 4 hours
220    private static final long NTP_INTERVAL = 4*60*60*1000;
221    // how long to wait if we have a network error in NTP or XTRA downloading
222    // current setting - 5 minutes
223    private static final long RETRY_INTERVAL = 5*60*1000;
224
225    private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
226        public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
227            if (listener == null) {
228                throw new NullPointerException("listener is null in addGpsStatusListener");
229            }
230
231            synchronized(mListeners) {
232                IBinder binder = listener.asBinder();
233                int size = mListeners.size();
234                for (int i = 0; i < size; i++) {
235                    Listener test = mListeners.get(i);
236                    if (binder.equals(test.mListener.asBinder())) {
237                        // listener already added
238                        return;
239                    }
240                }
241
242                Listener l = new Listener(listener);
243                binder.linkToDeath(l, 0);
244                mListeners.add(l);
245            }
246        }
247
248        public void removeGpsStatusListener(IGpsStatusListener listener) {
249            if (listener == null) {
250                throw new NullPointerException("listener is null in addGpsStatusListener");
251            }
252
253            synchronized(mListeners) {
254                IBinder binder = listener.asBinder();
255                Listener l = null;
256                int size = mListeners.size();
257                for (int i = 0; i < size && l == null; i++) {
258                    Listener test = mListeners.get(i);
259                    if (binder.equals(test.mListener.asBinder())) {
260                        l = test;
261                    }
262                }
263
264                if (l != null) {
265                    mListeners.remove(l);
266                    binder.unlinkToDeath(l, 0);
267                }
268            }
269        }
270    };
271
272    public IGpsStatusProvider getGpsStatusProvider() {
273        return mGpsStatusProvider;
274    }
275
276    private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
277        @Override public void onReceive(Context context, Intent intent) {
278            String action = intent.getAction();
279
280            if (action.equals(ALARM_WAKEUP)) {
281                if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
282                startNavigating();
283            } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
284                String state = intent.getStringExtra(Phone.STATE_KEY);
285                String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
286                String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
287
288                if (Config.LOGD) {
289                    Log.d(TAG, "state: " + state +  " apnName: " + apnName + " reason: " + reason);
290                }
291                if ("CONNECTED".equals(state) && apnName != null && apnName.length() > 0) {
292                    mSuplApn = apnName;
293                    if (mSuplDataConnectionState == SUPL_DATA_CONNECTION_OPENING) {
294                        native_supl_data_conn_open(mSuplApn);
295                        mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPEN;
296                    }
297                }
298            }
299        }
300    };
301
302    public static boolean isSupported() {
303        return native_is_supported();
304    }
305
306    public GpsLocationProvider(Context context, ILocationManager locationManager) {
307        mContext = context;
308        mLocationManager = locationManager;
309
310        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
311        mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
312
313        IntentFilter intentFilter = new IntentFilter();
314        intentFilter.addAction(ALARM_WAKEUP);
315        intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
316        context.registerReceiver(mBroadcastReciever, intentFilter);
317
318        mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
319
320        // Battery statistics service to be notified when GPS turns on or off
321        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
322
323        mProperties = new Properties();
324        try {
325            File file = new File(PROPERTIES_FILE);
326            FileInputStream stream = new FileInputStream(file);
327            mProperties.load(stream);
328            stream.close();
329            mNtpServer = mProperties.getProperty("NTP_SERVER", null);
330            mSuplHost = mProperties.getProperty("SUPL_HOST");
331            String suplPortString = mProperties.getProperty("SUPL_PORT");
332            if (mSuplHost != null && suplPortString != null) {
333                try {
334                    mSuplPort = Integer.parseInt(suplPortString);
335                    mSetSuplServer = true;
336                } catch (NumberFormatException e) {
337                    Log.e(TAG, "unable to parse SUPL_PORT: " + suplPortString);
338                }
339            }
340        } catch (IOException e) {
341            Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
342        }
343    }
344
345    /**
346     * Returns true if the provider requires access to a
347     * data network (e.g., the Internet), false otherwise.
348     */
349    public boolean requiresNetwork() {
350        // We want updateNetworkState() to get called when the network state changes
351        // for XTRA and NTP time injection support.
352        return (mNtpServer != null || native_supports_xtra() || mSuplHost != null);
353    }
354
355    public void updateNetworkState(int state) {
356        mNetworkAvailable = (state == LocationProvider.AVAILABLE);
357
358        if (Config.LOGD) {
359            Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable"));
360        }
361
362        if (mNetworkAvailable && mNetworkThread != null && mEnabled) {
363            // signal the network thread when the network becomes available
364            mNetworkThread.signal();
365        }
366    }
367
368    /**
369     * Returns true if the provider requires access to a
370     * satellite-based positioning system (e.g., GPS), false
371     * otherwise.
372     */
373    public boolean requiresSatellite() {
374        return true;
375    }
376
377    /**
378     * Returns true if the provider requires access to an appropriate
379     * cellular network (e.g., to make use of cell tower IDs), false
380     * otherwise.
381     */
382    public boolean requiresCell() {
383        return false;
384    }
385
386    /**
387     * Returns true if the use of this provider may result in a
388     * monetary charge to the user, false if use is free.  It is up to
389     * each provider to give accurate information.
390     */
391    public boolean hasMonetaryCost() {
392        return false;
393    }
394
395    /**
396     * Returns true if the provider is able to provide altitude
397     * information, false otherwise.  A provider that reports altitude
398     * under most circumstances but may occassionally not report it
399     * should return true.
400     */
401    public boolean supportsAltitude() {
402        return true;
403    }
404
405    /**
406     * Returns true if the provider is able to provide speed
407     * information, false otherwise.  A provider that reports speed
408     * under most circumstances but may occassionally not report it
409     * should return true.
410     */
411    public boolean supportsSpeed() {
412        return true;
413    }
414
415    /**
416     * Returns true if the provider is able to provide bearing
417     * information, false otherwise.  A provider that reports bearing
418     * under most circumstances but may occassionally not report it
419     * should return true.
420     */
421    public boolean supportsBearing() {
422        return true;
423    }
424
425    /**
426     * Returns the power requirement for this provider.
427     *
428     * @return the power requirement for this provider, as one of the
429     * constants Criteria.POWER_REQUIREMENT_*.
430     */
431    public int getPowerRequirement() {
432        return Criteria.POWER_HIGH;
433    }
434
435    /**
436     * Returns the horizontal accuracy of this provider
437     *
438     * @return the accuracy of location from this provider, as one
439     * of the constants Criteria.ACCURACY_*.
440     */
441    public int getAccuracy() {
442        return Criteria.ACCURACY_FINE;
443    }
444
445    /**
446     * Enables this provider.  When enabled, calls to getStatus()
447     * must be handled.  Hardware may be started up
448     * when the provider is enabled.
449     */
450    public synchronized void enable() {
451        if (Config.LOGD) Log.d(TAG, "enable");
452        if (mEnabled) return;
453        mEnabled = native_init();
454
455        if (mEnabled) {
456            // run event listener thread while we are enabled
457            mEventThread = new GpsEventThread();
458            mEventThread.start();
459
460            if (requiresNetwork()) {
461                // run network thread for NTP and XTRA support
462                if (mNetworkThread == null) {
463                    mNetworkThread = new GpsNetworkThread();
464                    mNetworkThread.start();
465                } else {
466                    mNetworkThread.signal();
467                }
468            }
469        } else {
470            Log.w(TAG, "Failed to enable location provider");
471        }
472    }
473
474    /**
475     * Disables this provider.  When disabled, calls to getStatus()
476     * need not be handled.  Hardware may be shut
477     * down while the provider is disabled.
478     */
479    public synchronized void disable() {
480        if (Config.LOGD) Log.d(TAG, "disable");
481        if (!mEnabled) return;
482
483        mEnabled = false;
484        stopNavigating();
485        native_disable();
486
487        // make sure our event thread exits
488        if (mEventThread != null) {
489            try {
490                mEventThread.join();
491            } catch (InterruptedException e) {
492                Log.w(TAG, "InterruptedException when joining mEventThread");
493            }
494            mEventThread = null;
495        }
496
497        if (mNetworkThread != null) {
498            mNetworkThread.setDone();
499            mNetworkThread = null;
500        }
501
502        // The GpsEventThread does not wait for the GPS to shutdown
503        // so we need to report the GPS_STATUS_ENGINE_OFF event here
504        if (mNavigating) {
505            reportStatus(GPS_STATUS_ENGINE_OFF);
506        }
507
508        native_cleanup();
509    }
510
511    public boolean isEnabled() {
512        return mEnabled;
513    }
514
515    public int getStatus(Bundle extras) {
516        if (extras != null) {
517            extras.putInt("satellites", mSvCount);
518        }
519        return mStatus;
520    }
521
522    private void updateStatus(int status, int svCount) {
523        if (status != mStatus || svCount != mSvCount) {
524            mStatus = status;
525            mSvCount = svCount;
526            mLocationExtras.putInt("satellites", svCount);
527            mStatusUpdateTime = SystemClock.elapsedRealtime();
528        }
529    }
530
531    public long getStatusUpdateTime() {
532        return mStatusUpdateTime;
533    }
534
535    public void enableLocationTracking(boolean enable) {
536        if (enable) {
537            mTTFF = 0;
538            mLastFixTime = 0;
539            startNavigating();
540        } else {
541            mAlarmManager.cancel(mWakeupIntent);
542            stopNavigating();
543        }
544    }
545
546    public void setMinTime(long minTime) {
547        if (Config.LOGD) Log.d(TAG, "setMinTime " + minTime);
548
549        if (minTime >= 0) {
550            int interval = (int)(minTime/1000);
551            if (interval < 1) {
552                interval = 1;
553            }
554            mFixInterval = interval;
555            native_set_fix_frequency(mFixInterval);
556        }
557    }
558
559    private final class Listener implements IBinder.DeathRecipient {
560        final IGpsStatusListener mListener;
561
562        int mSensors = 0;
563
564        Listener(IGpsStatusListener listener) {
565            mListener = listener;
566        }
567
568        public void binderDied() {
569            if (Config.LOGD) Log.d(TAG, "GPS status listener died");
570
571            synchronized(mListeners) {
572                mListeners.remove(this);
573            }
574        }
575    }
576
577    public void wakeLockAcquired() {
578    }
579
580    public void wakeLockReleased() {
581    }
582
583    public void addListener(int uid) {
584        mClientUids.put(uid, 0);
585        if (mNavigating) {
586            try {
587                mBatteryStats.noteStartGps(uid);
588            } catch (RemoteException e) {
589                Log.w(TAG, "RemoteException in addListener");
590            }
591        }
592    }
593
594    public void removeListener(int uid) {
595        mClientUids.delete(uid);
596        if (mNavigating) {
597            try {
598                mBatteryStats.noteStopGps(uid);
599            } catch (RemoteException e) {
600                Log.w(TAG, "RemoteException in removeListener");
601            }
602        }
603    }
604
605    public boolean sendExtraCommand(String command, Bundle extras) {
606
607        if ("delete_aiding_data".equals(command)) {
608            return deleteAidingData(extras);
609        }
610
611        Log.w(TAG, "sendExtraCommand: unknown command " + command);
612        return false;
613    }
614
615    private boolean deleteAidingData(Bundle extras) {
616        int flags;
617
618        if (extras == null) {
619            flags = GPS_DELETE_ALL;
620        } else {
621            flags = 0;
622            if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
623            if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
624            if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
625            if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
626            if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
627            if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
628            if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
629            if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
630            if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
631            if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
632            if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
633            if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
634            if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
635        }
636
637        if (flags != 0) {
638            native_delete_aiding_data(flags);
639            return true;
640        }
641
642        return false;
643    }
644
645    public void startNavigating() {
646        if (!mStarted) {
647            if (DEBUG) Log.d(TAG, "startNavigating");
648            mStarted = true;
649            if (!native_start(mPositionMode, false, mFixInterval)) {
650                mStarted = false;
651                Log.e(TAG, "native_start failed in startNavigating()");
652            }
653
654            // reset SV count to zero
655            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
656            mFixCount = 0;
657            mFixRequestTime = System.currentTimeMillis();
658        }
659    }
660
661    public void stopNavigating() {
662        if (DEBUG) Log.d(TAG, "stopNavigating");
663        if (mStarted) {
664            mStarted = false;
665            native_stop();
666            mTTFF = 0;
667            mLastFixTime = 0;
668            mLocationFlags = LOCATION_INVALID;
669
670            // reset SV count to zero
671            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
672        }
673    }
674
675    /**
676     * called from native code to update our position.
677     */
678    private void reportLocation(int flags, double latitude, double longitude, double altitude,
679            float speed, float bearing, float accuracy, long timestamp) {
680        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
681                " timestamp: " + timestamp);
682
683        mLastFixTime = System.currentTimeMillis();
684        // report time to first fix
685        if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
686            mTTFF = (int)(mLastFixTime - mFixRequestTime);
687            if (Config.LOGD) Log.d(TAG, "TTFF: " + mTTFF);
688
689            // notify status listeners
690            synchronized(mListeners) {
691                int size = mListeners.size();
692                for (int i = 0; i < size; i++) {
693                    Listener listener = mListeners.get(i);
694                    try {
695                        listener.mListener.onFirstFix(mTTFF);
696                    } catch (RemoteException e) {
697                        Log.w(TAG, "RemoteException in stopNavigating");
698                        mListeners.remove(listener);
699                        // adjust for size of list changing
700                        size--;
701                    }
702                }
703            }
704        }
705
706        synchronized (mLocation) {
707            mLocationFlags = flags;
708            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
709                mLocation.setLatitude(latitude);
710                mLocation.setLongitude(longitude);
711                mLocation.setTime(timestamp);
712            }
713            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
714                mLocation.setAltitude(altitude);
715            } else {
716                mLocation.removeAltitude();
717            }
718            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
719                mLocation.setSpeed(speed);
720            } else {
721                mLocation.removeSpeed();
722            }
723            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
724                mLocation.setBearing(bearing);
725            } else {
726                mLocation.removeBearing();
727            }
728            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
729                mLocation.setAccuracy(accuracy);
730            } else {
731                mLocation.removeAccuracy();
732            }
733
734            try {
735                mLocationManager.reportLocation(mLocation);
736            } catch (RemoteException e) {
737                Log.e(TAG, "RemoteException calling reportLocation");
738            }
739        }
740
741        if (mStarted && mStatus != LocationProvider.AVAILABLE) {
742            // send an intent to notify that the GPS is receiving fixes.
743            Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
744            intent.putExtra(EXTRA_ENABLED, true);
745            mContext.sendBroadcast(intent);
746            updateStatus(LocationProvider.AVAILABLE, mSvCount);
747        }
748
749        if (mFixCount++ >= MIN_FIX_COUNT && mFixInterval > 1) {
750            if (DEBUG) Log.d(TAG, "exceeded MIN_FIX_COUNT");
751            stopNavigating();
752            mFixCount = 0;
753            mAlarmManager.cancel(mWakeupIntent);
754            long now = SystemClock.elapsedRealtime();
755            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
756                    SystemClock.elapsedRealtime() + mFixInterval * 1000, mWakeupIntent);
757        }
758   }
759
760    /**
761     * called from native code to update our status
762     */
763    private void reportStatus(int status) {
764        if (VERBOSE) Log.v(TAG, "reportStatus status: " + status);
765
766        boolean wasNavigating = mNavigating;
767        mNavigating = (status == GPS_STATUS_SESSION_BEGIN);
768
769        if (wasNavigating != mNavigating) {
770            synchronized(mListeners) {
771                int size = mListeners.size();
772                for (int i = 0; i < size; i++) {
773                    Listener listener = mListeners.get(i);
774                    try {
775                        if (mNavigating) {
776                            listener.mListener.onGpsStarted();
777                        } else {
778                            listener.mListener.onGpsStopped();
779                        }
780                    } catch (RemoteException e) {
781                        Log.w(TAG, "RemoteException in reportStatus");
782                        mListeners.remove(listener);
783                        // adjust for size of list changing
784                        size--;
785                    }
786                }
787            }
788
789            try {
790                // update battery stats
791                for (int i=mClientUids.size() - 1; i >= 0; i--) {
792                    int uid = mClientUids.keyAt(i);
793                    if (mNavigating) {
794                        mBatteryStats.noteStartGps(uid);
795                    } else {
796                        mBatteryStats.noteStopGps(uid);
797                    }
798                }
799            } catch (RemoteException e) {
800                Log.w(TAG, "RemoteException in reportStatus");
801            }
802
803            // send an intent to notify that the GPS has been enabled or disabled.
804            Intent intent = new Intent(GPS_ENABLED_CHANGE_ACTION);
805            intent.putExtra(EXTRA_ENABLED, mNavigating);
806            mContext.sendBroadcast(intent);
807        }
808    }
809
810    /**
811     * called from native code to update SV info
812     */
813    private void reportSvStatus() {
814
815        int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
816
817        synchronized(mListeners) {
818            int size = mListeners.size();
819            for (int i = 0; i < size; i++) {
820                Listener listener = mListeners.get(i);
821                try {
822                    listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
823                            mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
824                            mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
825                } catch (RemoteException e) {
826                    Log.w(TAG, "RemoteException in reportSvInfo");
827                    mListeners.remove(listener);
828                    // adjust for size of list changing
829                    size--;
830                }
831            }
832        }
833
834        if (VERBOSE) {
835            Log.v(TAG, "SV count: " + svCount +
836                    " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
837                    " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
838            for (int i = 0; i < svCount; i++) {
839                Log.v(TAG, "sv: " + mSvs[i] +
840                        " snr: " + (float)mSnrs[i]/10 +
841                        " elev: " + mSvElevations[i] +
842                        " azimuth: " + mSvAzimuths[i] +
843                        ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " E") +
844                        ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " A") +
845                        ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
846            }
847        }
848
849        updateStatus(mStatus, svCount);
850
851        if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
852            System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
853            // send an intent to notify that the GPS is no longer receiving fixes.
854            Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
855            intent.putExtra(EXTRA_ENABLED, false);
856            mContext.sendBroadcast(intent);
857            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
858        }
859    }
860
861    /**
862     * called from native code to update SUPL status
863     */
864    private void reportSuplStatus(int status) {
865        switch (status) {
866            case GPS_REQUEST_SUPL_DATA_CONN:
867                 int result = mConnMgr.startUsingNetworkFeature(
868                        ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
869                if (result == Phone.APN_ALREADY_ACTIVE) {
870                    native_supl_data_conn_open(mSuplApn);
871                    mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPEN;
872                } else if (result == Phone.APN_REQUEST_STARTED) {
873                    mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPENING;
874                } else {
875                    native_supl_data_conn_failed();
876                }
877                break;
878            case GPS_RELEASE_SUPL_DATA_CONN:
879                if (mSuplDataConnectionState != SUPL_DATA_CONNECTION_CLOSED) {
880                    mConnMgr.stopUsingNetworkFeature(
881                            ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
882                    native_supl_data_conn_closed();
883                    mSuplDataConnectionState = SUPL_DATA_CONNECTION_CLOSED;
884                }
885                break;
886            case GPS_SUPL_DATA_CONNECTED:
887                // Log.d(TAG, "GPS_SUPL_DATA_CONNECTED");
888                break;
889            case GPS_SUPL_DATA_CONN_DONE:
890                // Log.d(TAG, "GPS_SUPL_DATA_CONN_DONE");
891                break;
892            case GPS_SUPL_DATA_CONN_FAILED:
893                // Log.d(TAG, "GPS_SUPL_DATA_CONN_FAILED");
894                break;
895        }
896    }
897
898    private void xtraDownloadRequest() {
899        if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest");
900        if (mNetworkThread != null) {
901            mNetworkThread.xtraDownloadRequest();
902        }
903    }
904
905    private class GpsEventThread extends Thread {
906
907        public GpsEventThread() {
908            super("GpsEventThread");
909        }
910
911        public void run() {
912            if (Config.LOGD) Log.d(TAG, "GpsEventThread starting");
913            // Exit as soon as disable() is called instead of waiting for the GPS to stop.
914            while (mEnabled) {
915                // this will wait for an event from the GPS,
916                // which will be reported via reportLocation or reportStatus
917                native_wait_for_event();
918            }
919            if (Config.LOGD) Log.d(TAG, "GpsEventThread exiting");
920        }
921    }
922
923    private class GpsNetworkThread extends Thread {
924
925        private long mNextNtpTime = 0;
926        private long mNextXtraTime = 0;
927        private boolean mXtraDownloadRequested = false;
928        private boolean mDone = false;
929
930        public GpsNetworkThread() {
931            super("GpsNetworkThread");
932        }
933
934        public void run() {
935            synchronized (mNetworkThreadLock) {
936                if (!mDone) {
937                    runLocked();
938                }
939            }
940        }
941
942        public void runLocked() {
943            if (Config.LOGD) Log.d(TAG, "NetworkThread starting");
944
945            SntpClient client = new SntpClient();
946            GpsXtraDownloader xtraDownloader = null;
947
948            if (native_supports_xtra()) {
949                xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
950            }
951
952            // thread exits after disable() is called
953            while (!mDone) {
954                long waitTime = getWaitTime();
955                do {
956                    synchronized (this) {
957                        try {
958                            if (!mNetworkAvailable) {
959                                if (Config.LOGD) Log.d(TAG,
960                                        "NetworkThread wait for network");
961                                wait();
962                            } else if (waitTime > 0) {
963                                if (Config.LOGD) {
964                                    Log.d(TAG, "NetworkThread wait for " +
965                                            waitTime + "ms");
966                                }
967                                wait(waitTime);
968                            }
969                        } catch (InterruptedException e) {
970                            if (Config.LOGD) {
971                                Log.d(TAG, "InterruptedException in GpsNetworkThread");
972                            }
973                        }
974                    }
975                    waitTime = getWaitTime();
976                } while (!mDone && ((!mXtraDownloadRequested && !mSetSuplServer && waitTime > 0)
977                        || !mNetworkAvailable));
978                if (Config.LOGD) Log.d(TAG, "NetworkThread out of wake loop");
979
980                if (!mDone) {
981                    if (mNtpServer != null &&
982                            mNextNtpTime <= System.currentTimeMillis()) {
983                        if (Config.LOGD) {
984                            Log.d(TAG, "Requesting time from NTP server " + mNtpServer);
985                        }
986                        if (client.requestTime(mNtpServer, 10000)) {
987                            long time = client.getNtpTime();
988                            long timeReference = client.getNtpTimeReference();
989                            int certainty = (int)(client.getRoundTripTime()/2);
990
991                            if (Config.LOGD) Log.d(TAG, "calling native_inject_time: " +
992                                    time + " reference: " + timeReference
993                                    + " certainty: " + certainty);
994
995                            native_inject_time(time, timeReference, certainty);
996                            mNextNtpTime = System.currentTimeMillis() + NTP_INTERVAL;
997                        } else {
998                            if (Config.LOGD) Log.d(TAG, "requestTime failed");
999                            mNextNtpTime = System.currentTimeMillis() + RETRY_INTERVAL;
1000                        }
1001                    }
1002
1003                    // Set the SUPL server address if we have not yet
1004                    if (mSetSuplServer) {
1005                        try {
1006                            InetAddress inetAddress = InetAddress.getByName(mSuplHost);
1007                            if (inetAddress != null) {
1008                                byte[] addrBytes = inetAddress.getAddress();
1009                                long addr = 0;
1010                                for (int i = 0; i < addrBytes.length; i++) {
1011                                    int temp = addrBytes[i];
1012                                    // signed -> unsigned
1013                                    if (temp < 0) temp = 256 + temp;
1014                                    addr = addr * 256 + temp;
1015                                }
1016                                // use MS-Based position mode if SUPL support is enabled
1017                                mPositionMode = GPS_POSITION_MODE_MS_BASED;
1018                                native_set_supl_server((int)addr, mSuplPort);
1019                                mSetSuplServer = false;
1020                            }
1021                        } catch (UnknownHostException e) {
1022                            Log.e(TAG, "unknown host for SUPL server " + mSuplHost);
1023                        }
1024                    }
1025
1026                    if ((mXtraDownloadRequested ||
1027                            (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis()))
1028                            && xtraDownloader != null) {
1029                        byte[] data = xtraDownloader.downloadXtraData();
1030                        if (data != null) {
1031                            if (Config.LOGD) {
1032                                Log.d(TAG, "calling native_inject_xtra_data");
1033                            }
1034                            native_inject_xtra_data(data, data.length);
1035                            mNextXtraTime = 0;
1036                            mXtraDownloadRequested = false;
1037                        } else {
1038                            mNextXtraTime = System.currentTimeMillis() + RETRY_INTERVAL;
1039                        }
1040                    }
1041                }
1042            }
1043            if (Config.LOGD) Log.d(TAG, "NetworkThread exiting");
1044        }
1045
1046        synchronized void xtraDownloadRequest() {
1047            mXtraDownloadRequested = true;
1048            notify();
1049        }
1050
1051        synchronized void signal() {
1052            notify();
1053        }
1054
1055        synchronized void setDone() {
1056            if (Config.LOGD) Log.d(TAG, "stopping NetworkThread");
1057            mDone = true;
1058            notify();
1059        }
1060
1061        private long getWaitTime() {
1062            long now = System.currentTimeMillis();
1063            long waitTime = Long.MAX_VALUE;
1064            if (mNtpServer != null) {
1065                waitTime = mNextNtpTime - now;
1066            }
1067            if (mNextXtraTime != 0) {
1068                long xtraWaitTime = mNextXtraTime - now;
1069                if (xtraWaitTime < waitTime) {
1070                    waitTime = xtraWaitTime;
1071                }
1072            }
1073            if (waitTime < 0) {
1074                waitTime = 0;
1075            }
1076            return waitTime;
1077        }
1078    }
1079
1080    // for GPS SV statistics
1081    private static final int MAX_SVS = 32;
1082    private static final int EPHEMERIS_MASK = 0;
1083    private static final int ALMANAC_MASK = 1;
1084    private static final int USED_FOR_FIX_MASK = 2;
1085
1086    // preallocated arrays, to avoid memory allocation in reportStatus()
1087    private int mSvs[] = new int[MAX_SVS];
1088    private float mSnrs[] = new float[MAX_SVS];
1089    private float mSvElevations[] = new float[MAX_SVS];
1090    private float mSvAzimuths[] = new float[MAX_SVS];
1091    private int mSvMasks[] = new int[3];
1092    private int mSvCount;
1093
1094    static { class_init_native(); }
1095    private static native void class_init_native();
1096    private static native boolean native_is_supported();
1097
1098    private native boolean native_init();
1099    private native void native_disable();
1100    private native void native_cleanup();
1101    private native boolean native_start(int positionMode, boolean singleFix, int fixInterval);
1102    private native boolean native_stop();
1103    private native void native_set_fix_frequency(int fixFrequency);
1104    private native void native_delete_aiding_data(int flags);
1105    private native void native_wait_for_event();
1106    // returns number of SVs
1107    // mask[0] is ephemeris mask and mask[1] is almanac mask
1108    private native int native_read_sv_status(int[] svs, float[] snrs,
1109            float[] elevations, float[] azimuths, int[] masks);
1110
1111    // XTRA Support
1112    private native void native_inject_time(long time, long timeReference, int uncertainty);
1113    private native boolean native_supports_xtra();
1114    private native void native_inject_xtra_data(byte[] data, int length);
1115
1116    // SUPL Support
1117    private native void native_supl_data_conn_open(String apn);
1118    private native void native_supl_data_conn_closed();
1119    private native void native_supl_data_conn_failed();
1120    private native void native_set_supl_server(int addr, int port);
1121}
1122