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