GpsLocationProvider.java revision 03ca216ac19ea4e7afcb183c20c7c780f0d97756
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.server.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.INetInitiatedListener;
30import android.location.Location;
31import android.location.LocationManager;
32import android.location.LocationProvider;
33import android.net.ConnectivityManager;
34import android.net.NetworkInfo;
35import android.net.SntpClient;
36import android.os.Binder;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.Message;
42import android.os.PowerManager;
43import android.os.Process;
44import android.os.RemoteException;
45import android.os.ServiceManager;
46import android.os.SystemClock;
47import android.provider.Settings;
48import android.util.Log;
49import android.util.SparseIntArray;
50
51import com.android.internal.app.IBatteryStats;
52import com.android.internal.telephony.Phone;
53import com.android.internal.location.GpsNetInitiatedHandler;
54import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
55
56import java.io.File;
57import java.io.FileInputStream;
58import java.io.IOException;
59import java.io.StringBufferInputStream;
60import java.net.InetAddress;
61import java.net.UnknownHostException;
62import java.util.ArrayList;
63import java.util.Date;
64import java.util.Properties;
65import java.util.Map.Entry;
66import java.util.concurrent.CountDownLatch;
67
68/**
69 * A GPS implementation of LocationProvider used by LocationManager.
70 *
71 * {@hide}
72 */
73public class GpsLocationProvider implements LocationProviderInterface {
74
75    private static final String TAG = "GpsLocationProvider";
76
77    private static final boolean DEBUG = false;
78    private static final boolean VERBOSE = false;
79
80    // these need to match GpsPositionMode enum in gps.h
81    private static final int GPS_POSITION_MODE_STANDALONE = 0;
82    private static final int GPS_POSITION_MODE_MS_BASED = 1;
83    private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
84
85    // these need to match GpsPositionRecurrence enum in gps.h
86    private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
87    private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
88
89    // these need to match GpsStatusValue defines in gps.h
90    private static final int GPS_STATUS_NONE = 0;
91    private static final int GPS_STATUS_SESSION_BEGIN = 1;
92    private static final int GPS_STATUS_SESSION_END = 2;
93    private static final int GPS_STATUS_ENGINE_ON = 3;
94    private static final int GPS_STATUS_ENGINE_OFF = 4;
95
96    // these need to match GpsApgsStatusValue defines in gps.h
97    /** AGPS status event values. */
98    private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
99    private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
100    private static final int GPS_AGPS_DATA_CONNECTED = 3;
101    private static final int GPS_AGPS_DATA_CONN_DONE = 4;
102    private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
103
104    // these need to match GpsLocationFlags enum in gps.h
105    private static final int LOCATION_INVALID = 0;
106    private static final int LOCATION_HAS_LAT_LONG = 1;
107    private static final int LOCATION_HAS_ALTITUDE = 2;
108    private static final int LOCATION_HAS_SPEED = 4;
109    private static final int LOCATION_HAS_BEARING = 8;
110    private static final int LOCATION_HAS_ACCURACY = 16;
111
112// IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
113    private static final int GPS_DELETE_EPHEMERIS = 0x0001;
114    private static final int GPS_DELETE_ALMANAC = 0x0002;
115    private static final int GPS_DELETE_POSITION = 0x0004;
116    private static final int GPS_DELETE_TIME = 0x0008;
117    private static final int GPS_DELETE_IONO = 0x0010;
118    private static final int GPS_DELETE_UTC = 0x0020;
119    private static final int GPS_DELETE_HEALTH = 0x0040;
120    private static final int GPS_DELETE_SVDIR = 0x0080;
121    private static final int GPS_DELETE_SVSTEER = 0x0100;
122    private static final int GPS_DELETE_SADATA = 0x0200;
123    private static final int GPS_DELETE_RTI = 0x0400;
124    private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
125    private static final int GPS_DELETE_ALL = 0xFFFF;
126
127    // The GPS_CAPABILITY_* flags must match the values in gps.h
128    private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
129    private static final int GPS_CAPABILITY_MSB = 0x0000002;
130    private static final int GPS_CAPABILITY_MSA = 0x0000004;
131    private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
132
133
134    // these need to match AGpsType enum in gps.h
135    private static final int AGPS_TYPE_SUPL = 1;
136    private static final int AGPS_TYPE_C2K = 2;
137
138    // for mAGpsDataConnectionState
139    private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
140    private static final int AGPS_DATA_CONNECTION_OPENING = 1;
141    private static final int AGPS_DATA_CONNECTION_OPEN = 2;
142
143    // Handler messages
144    private static final int CHECK_LOCATION = 1;
145    private static final int ENABLE = 2;
146    private static final int ENABLE_TRACKING = 3;
147    private static final int UPDATE_NETWORK_STATE = 4;
148    private static final int INJECT_NTP_TIME = 5;
149    private static final int DOWNLOAD_XTRA_DATA = 6;
150    private static final int UPDATE_LOCATION = 7;
151    private static final int ADD_LISTENER = 8;
152    private static final int REMOVE_LISTENER = 9;
153    private static final int REQUEST_SINGLE_SHOT = 10;
154
155    private static final String PROPERTIES_FILE = "/etc/gps.conf";
156
157    private int mLocationFlags = LOCATION_INVALID;
158
159    // current status
160    private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
161
162    // time for last status update
163    private long mStatusUpdateTime = SystemClock.elapsedRealtime();
164
165    // turn off GPS fix icon if we haven't received a fix in 10 seconds
166    private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
167
168    // stop trying if we do not receive a fix within 60 seconds
169    private static final int NO_FIX_TIMEOUT = 60 * 1000;
170
171    // true if we are enabled
172    private volatile boolean mEnabled;
173
174    // true if we have network connectivity
175    private boolean mNetworkAvailable;
176
177    // flags to trigger NTP or XTRA data download when network becomes available
178    // initialized to true so we do NTP and XTRA when the network comes up after booting
179    private boolean mInjectNtpTimePending = true;
180    private boolean mDownloadXtraDataPending = true;
181
182    // true if GPS is navigating
183    private boolean mNavigating;
184
185    // true if GPS engine is on
186    private boolean mEngineOn;
187
188    // requested frequency of fixes, in milliseconds
189    private int mFixInterval = 1000;
190
191    // true if we started navigation
192    private boolean mStarted;
193
194    // true if single shot request is in progress
195    private boolean mSingleShot;
196
197    // capabilities of the GPS engine
198    private int mEngineCapabilities;
199
200    // for calculating time to first fix
201    private long mFixRequestTime = 0;
202    // time to first fix for most recent session
203    private int mTTFF = 0;
204    // time we received our last fix
205    private long mLastFixTime;
206
207    private int mPositionMode;
208
209    // properties loaded from PROPERTIES_FILE
210    private Properties mProperties;
211    private String mNtpServer;
212    private String mSuplServerHost;
213    private int mSuplServerPort;
214    private String mC2KServerHost;
215    private int mC2KServerPort;
216
217    private final Context mContext;
218    private final ILocationManager mLocationManager;
219    private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
220    private Bundle mLocationExtras = new Bundle();
221    private ArrayList<Listener> mListeners = new ArrayList<Listener>();
222
223    // GpsLocationProvider's handler thread
224    private final Thread mThread;
225    // Handler for processing events in mThread.
226    private Handler mHandler;
227    // Used to signal when our main thread has initialized everything
228    private final CountDownLatch mInitializedLatch = new CountDownLatch(1);
229    // Thread for receiving events from the native code
230    private Thread mEventThread;
231
232    private String mAGpsApn;
233    private int mAGpsDataConnectionState;
234    private final ConnectivityManager mConnMgr;
235    private final GpsNetInitiatedHandler mNIHandler;
236
237    // Wakelocks
238    private final static String WAKELOCK_KEY = "GpsLocationProvider";
239    private final PowerManager.WakeLock mWakeLock;
240    // bitfield of pending messages to our Handler
241    // used only for messages that cannot have multiple instances queued
242    private int mPendingMessageBits;
243    // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
244    // which might have multiple instances queued
245    private int mPendingListenerMessages;
246
247    // Alarms
248    private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
249    private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
250    private final AlarmManager mAlarmManager;
251    private final PendingIntent mWakeupIntent;
252    private final PendingIntent mTimeoutIntent;
253
254    private final IBatteryStats mBatteryStats;
255    private final SparseIntArray mClientUids = new SparseIntArray();
256
257    // how often to request NTP time, in milliseconds
258    // current setting 4 hours
259    private static final long NTP_INTERVAL = 4*60*60*1000;
260    // how long to wait if we have a network error in NTP or XTRA downloading
261    // current setting - 5 minutes
262    private static final long RETRY_INTERVAL = 5*60*1000;
263
264    // to avoid injecting bad NTP time, we reject any time fixes that differ from system time
265    // by more than 5 minutes.
266    private static final long MAX_NTP_SYSTEM_TIME_OFFSET = 5*60*1000;
267
268    private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
269        public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
270            if (listener == null) {
271                throw new NullPointerException("listener is null in addGpsStatusListener");
272            }
273
274            synchronized(mListeners) {
275                IBinder binder = listener.asBinder();
276                int size = mListeners.size();
277                for (int i = 0; i < size; i++) {
278                    Listener test = mListeners.get(i);
279                    if (binder.equals(test.mListener.asBinder())) {
280                        // listener already added
281                        return;
282                    }
283                }
284
285                Listener l = new Listener(listener);
286                binder.linkToDeath(l, 0);
287                mListeners.add(l);
288            }
289        }
290
291        public void removeGpsStatusListener(IGpsStatusListener listener) {
292            if (listener == null) {
293                throw new NullPointerException("listener is null in addGpsStatusListener");
294            }
295
296            synchronized(mListeners) {
297                IBinder binder = listener.asBinder();
298                Listener l = null;
299                int size = mListeners.size();
300                for (int i = 0; i < size && l == null; i++) {
301                    Listener test = mListeners.get(i);
302                    if (binder.equals(test.mListener.asBinder())) {
303                        l = test;
304                    }
305                }
306
307                if (l != null) {
308                    mListeners.remove(l);
309                    binder.unlinkToDeath(l, 0);
310                }
311            }
312        }
313    };
314
315    public IGpsStatusProvider getGpsStatusProvider() {
316        return mGpsStatusProvider;
317    }
318
319    private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
320        @Override public void onReceive(Context context, Intent intent) {
321            String action = intent.getAction();
322
323            if (action.equals(ALARM_WAKEUP)) {
324                if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
325                startNavigating(false);
326            } else if (action.equals(ALARM_TIMEOUT)) {
327                if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
328                hibernate();
329            }
330        }
331    };
332
333    public static boolean isSupported() {
334        return native_is_supported();
335    }
336
337    public GpsLocationProvider(Context context, ILocationManager locationManager) {
338        mContext = context;
339        mLocationManager = locationManager;
340        mNIHandler = new GpsNetInitiatedHandler(context);
341
342        mLocation.setExtras(mLocationExtras);
343
344        // Create a wake lock
345        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
346        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
347        mWakeLock.setReferenceCounted(false);
348
349        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
350        mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
351        mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
352
353        mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
354
355        // Battery statistics service to be notified when GPS turns on or off
356        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
357
358        mProperties = new Properties();
359        try {
360            File file = new File(PROPERTIES_FILE);
361            FileInputStream stream = new FileInputStream(file);
362            mProperties.load(stream);
363            stream.close();
364            mNtpServer = mProperties.getProperty("NTP_SERVER", null);
365
366            mSuplServerHost = mProperties.getProperty("SUPL_HOST");
367            String portString = mProperties.getProperty("SUPL_PORT");
368            if (mSuplServerHost != null && portString != null) {
369                try {
370                    mSuplServerPort = Integer.parseInt(portString);
371                } catch (NumberFormatException e) {
372                    Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
373                }
374            }
375
376            mC2KServerHost = mProperties.getProperty("C2K_HOST");
377            portString = mProperties.getProperty("C2K_PORT");
378            if (mC2KServerHost != null && portString != null) {
379                try {
380                    mC2KServerPort = Integer.parseInt(portString);
381                } catch (NumberFormatException e) {
382                    Log.e(TAG, "unable to parse C2K_PORT: " + portString);
383                }
384            }
385        } catch (IOException e) {
386            Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
387        }
388
389        // wait until we are fully initialized before returning
390        mThread = new GpsLocationProviderThread();
391        mThread.start();
392        while (true) {
393            try {
394                mInitializedLatch.await();
395                break;
396            } catch (InterruptedException e) {
397                Thread.currentThread().interrupt();
398            }
399        }
400    }
401
402    private void initialize() {
403        // register our receiver on our thread rather than the main thread
404        IntentFilter intentFilter = new IntentFilter();
405        intentFilter.addAction(ALARM_WAKEUP);
406        intentFilter.addAction(ALARM_TIMEOUT);
407        mContext.registerReceiver(mBroadcastReciever, intentFilter);
408    }
409
410    /**
411     * Returns the name of this provider.
412     */
413    public String getName() {
414        return LocationManager.GPS_PROVIDER;
415    }
416
417    /**
418     * Returns true if the provider requires access to a
419     * data network (e.g., the Internet), false otherwise.
420     */
421    public boolean requiresNetwork() {
422        return true;
423    }
424
425    public void updateNetworkState(int state, NetworkInfo info) {
426        sendMessage(UPDATE_NETWORK_STATE, state, info);
427    }
428
429    private void handleUpdateNetworkState(int state, NetworkInfo info) {
430        mNetworkAvailable = (state == LocationProvider.AVAILABLE);
431
432        if (DEBUG) {
433            Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
434                + " info: " + info);
435        }
436
437        if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
438                && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
439            String apnName = info.getExtraInfo();
440            if (mNetworkAvailable && apnName != null && apnName.length() > 0) {
441                mAGpsApn = apnName;
442                if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
443                native_agps_data_conn_open(apnName);
444                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
445            } else {
446                if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
447                mAGpsApn = null;
448                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
449                native_agps_data_conn_failed();
450            }
451        }
452
453        if (mNetworkAvailable) {
454            if (mInjectNtpTimePending) {
455                sendMessage(INJECT_NTP_TIME, 0, null);
456            }
457            if (mDownloadXtraDataPending) {
458                sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
459            }
460        }
461    }
462
463    private void handleInjectNtpTime() {
464        if (!mNetworkAvailable) {
465            // try again when network is up
466            mInjectNtpTimePending = true;
467            return;
468        }
469        mInjectNtpTimePending = false;
470
471        SntpClient client = new SntpClient();
472        long delay;
473
474        if (client.requestTime(mNtpServer, 10000)) {
475            long time = client.getNtpTime();
476            long timeReference = client.getNtpTimeReference();
477            int certainty = (int)(client.getRoundTripTime()/2);
478            long now = System.currentTimeMillis();
479
480            Log.d(TAG, "NTP server returned: "
481                    + time + " (" + new Date(time)
482                    + ") reference: " + timeReference
483                    + " certainty: " + certainty
484                    + " system time offset: " + (time - now));
485
486            native_inject_time(time, timeReference, certainty);
487            delay = NTP_INTERVAL;
488        } else {
489            if (DEBUG) Log.d(TAG, "requestTime failed");
490            delay = RETRY_INTERVAL;
491        }
492
493        // send delayed message for next NTP injection
494        // since this is delayed and not urgent we do not hold a wake lock here
495        mHandler.removeMessages(INJECT_NTP_TIME);
496        mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
497    }
498
499    private void handleDownloadXtraData() {
500        if (!mDownloadXtraDataPending) {
501            // try again when network is up
502            mDownloadXtraDataPending = true;
503            return;
504        }
505        mDownloadXtraDataPending = false;
506
507
508        GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
509        byte[] data = xtraDownloader.downloadXtraData();
510        if (data != null) {
511            if (DEBUG) {
512                Log.d(TAG, "calling native_inject_xtra_data");
513            }
514            native_inject_xtra_data(data, data.length);
515        } else {
516            // try again later
517            // since this is delayed and not urgent we do not hold a wake lock here
518            mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
519            mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
520        }
521    }
522
523    /**
524     * This is called to inform us when another location provider returns a location.
525     * Someday we might use this for network location injection to aid the GPS
526     */
527    public void updateLocation(Location location) {
528        sendMessage(UPDATE_LOCATION, 0, location);
529    }
530
531    private void handleUpdateLocation(Location location) {
532        if (location.hasAccuracy()) {
533            native_inject_location(location.getLatitude(), location.getLongitude(),
534                    location.getAccuracy());
535        }
536    }
537
538    /**
539     * Returns true if the provider requires access to a
540     * satellite-based positioning system (e.g., GPS), false
541     * otherwise.
542     */
543    public boolean requiresSatellite() {
544        return true;
545    }
546
547    /**
548     * Returns true if the provider requires access to an appropriate
549     * cellular network (e.g., to make use of cell tower IDs), false
550     * otherwise.
551     */
552    public boolean requiresCell() {
553        return false;
554    }
555
556    /**
557     * Returns true if the use of this provider may result in a
558     * monetary charge to the user, false if use is free.  It is up to
559     * each provider to give accurate information.
560     */
561    public boolean hasMonetaryCost() {
562        return false;
563    }
564
565    /**
566     * Returns true if the provider is able to provide altitude
567     * information, false otherwise.  A provider that reports altitude
568     * under most circumstances but may occassionally not report it
569     * should return true.
570     */
571    public boolean supportsAltitude() {
572        return true;
573    }
574
575    /**
576     * Returns true if the provider is able to provide speed
577     * information, false otherwise.  A provider that reports speed
578     * under most circumstances but may occassionally not report it
579     * should return true.
580     */
581    public boolean supportsSpeed() {
582        return true;
583    }
584
585    /**
586     * Returns true if the provider is able to provide bearing
587     * information, false otherwise.  A provider that reports bearing
588     * under most circumstances but may occassionally not report it
589     * should return true.
590     */
591    public boolean supportsBearing() {
592        return true;
593    }
594
595    /**
596     * Returns the power requirement for this provider.
597     *
598     * @return the power requirement for this provider, as one of the
599     * constants Criteria.POWER_REQUIREMENT_*.
600     */
601    public int getPowerRequirement() {
602        return Criteria.POWER_HIGH;
603    }
604
605    /**
606     * Returns true if this provider meets the given criteria,
607     * false otherwise.
608     */
609    public boolean meetsCriteria(Criteria criteria) {
610        return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
611    }
612
613    /**
614     * Returns the horizontal accuracy of this provider
615     *
616     * @return the accuracy of location from this provider, as one
617     * of the constants Criteria.ACCURACY_*.
618     */
619    public int getAccuracy() {
620        return Criteria.ACCURACY_FINE;
621    }
622
623    /**
624     * Enables this provider.  When enabled, calls to getStatus()
625     * must be handled.  Hardware may be started up
626     * when the provider is enabled.
627     */
628    public void enable() {
629        synchronized (mHandler) {
630            sendMessage(ENABLE, 1, null);
631        }
632    }
633
634    private void handleEnable() {
635        if (DEBUG) Log.d(TAG, "handleEnable");
636        if (mEnabled) return;
637        mEnabled = native_init();
638
639        if (mEnabled) {
640            if (mSuplServerHost != null) {
641                native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
642            }
643            if (mC2KServerHost != null) {
644                native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
645            }
646
647            // run event listener thread while we are enabled
648            mEventThread = new GpsEventThread();
649            mEventThread.start();
650        } else {
651            Log.w(TAG, "Failed to enable location provider");
652        }
653    }
654
655    /**
656     * Disables this provider.  When disabled, calls to getStatus()
657     * need not be handled.  Hardware may be shut
658     * down while the provider is disabled.
659     */
660    public void disable() {
661        synchronized (mHandler) {
662            sendMessage(ENABLE, 0, null);
663        }
664    }
665
666    private void handleDisable() {
667        if (DEBUG) Log.d(TAG, "handleDisable");
668        if (!mEnabled) return;
669
670        mEnabled = false;
671        stopNavigating();
672        native_disable();
673
674        // make sure our event thread exits
675        if (mEventThread != null) {
676            try {
677                mEventThread.join();
678            } catch (InterruptedException e) {
679                Log.w(TAG, "InterruptedException when joining mEventThread");
680            }
681            mEventThread = null;
682        }
683
684        // do this before releasing wakelock
685        native_cleanup();
686
687        // The GpsEventThread does not wait for the GPS to shutdown
688        // so we need to report the GPS_STATUS_ENGINE_OFF event here
689        if (mNavigating) {
690            reportStatus(GPS_STATUS_SESSION_END);
691        }
692        if (mEngineOn) {
693            reportStatus(GPS_STATUS_ENGINE_OFF);
694        }
695    }
696
697    public boolean isEnabled() {
698        return mEnabled;
699    }
700
701    public int getStatus(Bundle extras) {
702        if (extras != null) {
703            extras.putInt("satellites", mSvCount);
704        }
705        return mStatus;
706    }
707
708    private void updateStatus(int status, int svCount) {
709        if (status != mStatus || svCount != mSvCount) {
710            mStatus = status;
711            mSvCount = svCount;
712            mLocationExtras.putInt("satellites", svCount);
713            mStatusUpdateTime = SystemClock.elapsedRealtime();
714        }
715    }
716
717    public long getStatusUpdateTime() {
718        return mStatusUpdateTime;
719    }
720
721    public void enableLocationTracking(boolean enable) {
722        // FIXME - should set a flag here to avoid race conditions with single shot request
723        synchronized (mHandler) {
724            sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
725        }
726    }
727
728    private void handleEnableLocationTracking(boolean enable) {
729        if (enable) {
730            mTTFF = 0;
731            mLastFixTime = 0;
732            startNavigating(false);
733        } else {
734            if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
735                mAlarmManager.cancel(mWakeupIntent);
736                mAlarmManager.cancel(mTimeoutIntent);
737            }
738            stopNavigating();
739        }
740    }
741
742    public boolean requestSingleShotFix() {
743        if (mStarted) {
744            // cannot do single shot if already navigating
745            return false;
746        }
747        synchronized (mHandler) {
748            mHandler.removeMessages(REQUEST_SINGLE_SHOT);
749            Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
750            mHandler.sendMessage(m);
751        }
752        return true;
753    }
754
755    private void handleRequestSingleShot() {
756        mTTFF = 0;
757        mLastFixTime = 0;
758        startNavigating(true);
759    }
760
761    public void setMinTime(long minTime) {
762        if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
763
764        if (minTime >= 0) {
765            mFixInterval = (int)minTime;
766
767            if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
768                if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
769                        mFixInterval, 0, 0)) {
770                    Log.e(TAG, "set_position_mode failed in setMinTime()");
771                }
772            }
773        }
774    }
775
776    public String getInternalState() {
777        return native_get_internal_state();
778    }
779
780    private final class Listener implements IBinder.DeathRecipient {
781        final IGpsStatusListener mListener;
782
783        int mSensors = 0;
784
785        Listener(IGpsStatusListener listener) {
786            mListener = listener;
787        }
788
789        public void binderDied() {
790            if (DEBUG) Log.d(TAG, "GPS status listener died");
791
792            synchronized(mListeners) {
793                mListeners.remove(this);
794            }
795            if (mListener != null) {
796                mListener.asBinder().unlinkToDeath(this, 0);
797            }
798        }
799    }
800
801    public void addListener(int uid) {
802        synchronized (mWakeLock) {
803            mPendingListenerMessages++;
804           mWakeLock.acquire();
805            Message m = Message.obtain(mHandler, ADD_LISTENER);
806            m.arg1 = uid;
807            mHandler.sendMessage(m);
808        }
809    }
810
811    private void handleAddListener(int uid) {
812        synchronized(mListeners) {
813            if (mClientUids.indexOfKey(uid) >= 0) {
814                // Shouldn't be here -- already have this uid.
815                Log.w(TAG, "Duplicate add listener for uid " + uid);
816                return;
817            }
818            mClientUids.put(uid, 0);
819            if (mNavigating) {
820                try {
821                    mBatteryStats.noteStartGps(uid);
822                } catch (RemoteException e) {
823                    Log.w(TAG, "RemoteException in addListener");
824                }
825            }
826        }
827    }
828
829    public void removeListener(int uid) {
830        synchronized (mWakeLock) {
831            mPendingListenerMessages++;
832            mWakeLock.acquire();
833            Message m = Message.obtain(mHandler, REMOVE_LISTENER);
834            m.arg1 = uid;
835            mHandler.sendMessage(m);
836        }
837    }
838
839    private void handleRemoveListener(int uid) {
840        synchronized(mListeners) {
841            if (mClientUids.indexOfKey(uid) < 0) {
842                // Shouldn't be here -- don't have this uid.
843                Log.w(TAG, "Unneeded remove listener for uid " + uid);
844                return;
845            }
846            mClientUids.delete(uid);
847            if (mNavigating) {
848                try {
849                    mBatteryStats.noteStopGps(uid);
850                } catch (RemoteException e) {
851                    Log.w(TAG, "RemoteException in removeListener");
852                }
853            }
854        }
855    }
856
857    public boolean sendExtraCommand(String command, Bundle extras) {
858
859        long identity = Binder.clearCallingIdentity();
860        boolean result = false;
861
862        if ("delete_aiding_data".equals(command)) {
863            result = deleteAidingData(extras);
864        } else if ("force_time_injection".equals(command)) {
865            sendMessage(INJECT_NTP_TIME, 0, null);
866            result = true;
867        } else if ("force_xtra_injection".equals(command)) {
868            if (native_supports_xtra()) {
869                xtraDownloadRequest();
870                result = true;
871            }
872        } else {
873            Log.w(TAG, "sendExtraCommand: unknown command " + command);
874        }
875
876        Binder.restoreCallingIdentity(identity);
877        return result;
878    }
879
880    private boolean deleteAidingData(Bundle extras) {
881        int flags;
882
883        if (extras == null) {
884            flags = GPS_DELETE_ALL;
885        } else {
886            flags = 0;
887            if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
888            if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
889            if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
890            if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
891            if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
892            if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
893            if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
894            if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
895            if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
896            if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
897            if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
898            if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
899            if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
900        }
901
902        if (flags != 0) {
903            native_delete_aiding_data(flags);
904            return true;
905        }
906
907        return false;
908    }
909
910    private void startNavigating(boolean singleShot) {
911        if (!mStarted) {
912            if (DEBUG) Log.d(TAG, "startNavigating");
913            mStarted = true;
914            mSingleShot = singleShot;
915            mPositionMode = GPS_POSITION_MODE_STANDALONE;
916
917             if (Settings.Secure.getInt(mContext.getContentResolver(),
918                    Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
919                if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
920                    mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
921                } else if (hasCapability(GPS_CAPABILITY_MSA)) {
922                    mPositionMode = GPS_POSITION_MODE_MS_BASED;
923                }
924            }
925
926            int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
927            if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
928                    interval, 0, 0)) {
929                mStarted = false;
930                Log.e(TAG, "set_position_mode failed in startNavigating()");
931                return;
932            }
933            if (!native_start()) {
934                mStarted = false;
935                Log.e(TAG, "native_start failed in startNavigating()");
936                return;
937            }
938
939            // reset SV count to zero
940            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
941            mFixRequestTime = System.currentTimeMillis();
942            if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
943                // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
944                // and our fix interval is not short
945                if (mFixInterval >= NO_FIX_TIMEOUT) {
946                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
947                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
948                }
949            }
950        }
951    }
952
953    private void stopNavigating() {
954        if (DEBUG) Log.d(TAG, "stopNavigating");
955        if (mStarted) {
956            mStarted = false;
957            mSingleShot = false;
958            native_stop();
959            mTTFF = 0;
960            mLastFixTime = 0;
961            mLocationFlags = LOCATION_INVALID;
962
963            // reset SV count to zero
964            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
965        }
966    }
967
968    private void hibernate() {
969        // stop GPS until our next fix interval arrives
970        stopNavigating();
971        mAlarmManager.cancel(mTimeoutIntent);
972        mAlarmManager.cancel(mWakeupIntent);
973        long now = SystemClock.elapsedRealtime();
974        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
975                SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
976    }
977
978    private boolean hasCapability(int capability) {
979        return ((mEngineCapabilities & capability) != 0);
980    }
981
982    /**
983     * called from native code to update our position.
984     */
985    private void reportLocation(int flags, double latitude, double longitude, double altitude,
986            float speed, float bearing, float accuracy, long timestamp) {
987        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
988                " timestamp: " + timestamp);
989
990        synchronized (mLocation) {
991            mLocationFlags = flags;
992            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
993                mLocation.setLatitude(latitude);
994                mLocation.setLongitude(longitude);
995                mLocation.setTime(timestamp);
996            }
997            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
998                mLocation.setAltitude(altitude);
999            } else {
1000                mLocation.removeAltitude();
1001            }
1002            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1003                mLocation.setSpeed(speed);
1004            } else {
1005                mLocation.removeSpeed();
1006            }
1007            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1008                mLocation.setBearing(bearing);
1009            } else {
1010                mLocation.removeBearing();
1011            }
1012            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1013                mLocation.setAccuracy(accuracy);
1014            } else {
1015                mLocation.removeAccuracy();
1016            }
1017
1018            try {
1019                mLocationManager.reportLocation(mLocation, false);
1020            } catch (RemoteException e) {
1021                Log.e(TAG, "RemoteException calling reportLocation");
1022            }
1023        }
1024
1025        mLastFixTime = System.currentTimeMillis();
1026        // report time to first fix
1027        if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1028            mTTFF = (int)(mLastFixTime - mFixRequestTime);
1029            if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
1030
1031            // notify status listeners
1032            synchronized(mListeners) {
1033                int size = mListeners.size();
1034                for (int i = 0; i < size; i++) {
1035                    Listener listener = mListeners.get(i);
1036                    try {
1037                        listener.mListener.onFirstFix(mTTFF);
1038                    } catch (RemoteException e) {
1039                        Log.w(TAG, "RemoteException in stopNavigating");
1040                        mListeners.remove(listener);
1041                        // adjust for size of list changing
1042                        size--;
1043                    }
1044                }
1045            }
1046        }
1047
1048        if (mSingleShot) {
1049            stopNavigating();
1050        }
1051        if (mStarted && mStatus != LocationProvider.AVAILABLE) {
1052            // we want to time out if we do not receive a fix
1053            // within the time out and we are requesting infrequent fixes
1054            if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
1055                mAlarmManager.cancel(mTimeoutIntent);
1056            }
1057
1058            // send an intent to notify that the GPS is receiving fixes.
1059            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1060            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
1061            mContext.sendBroadcast(intent);
1062            updateStatus(LocationProvider.AVAILABLE, mSvCount);
1063        }
1064
1065       if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && mFixInterval > 1000) {
1066            if (DEBUG) Log.d(TAG, "got fix, hibernating");
1067            hibernate();
1068        }
1069   }
1070
1071    /**
1072     * called from native code to update our status
1073     */
1074    private void reportStatus(int status) {
1075        if (VERBOSE) Log.v(TAG, "reportStatus status: " + status);
1076
1077        synchronized(mListeners) {
1078            boolean wasNavigating = mNavigating;
1079
1080            switch (status) {
1081                case GPS_STATUS_SESSION_BEGIN:
1082                    mNavigating = true;
1083                    mEngineOn = true;
1084                    break;
1085                case GPS_STATUS_SESSION_END:
1086                    mNavigating = false;
1087                    break;
1088                case GPS_STATUS_ENGINE_ON:
1089                    mEngineOn = true;
1090                    break;
1091                case GPS_STATUS_ENGINE_OFF:
1092                    mEngineOn = false;
1093                    mNavigating = false;
1094                    break;
1095            }
1096
1097            if (wasNavigating != mNavigating) {
1098                int size = mListeners.size();
1099                for (int i = 0; i < size; i++) {
1100                    Listener listener = mListeners.get(i);
1101                    try {
1102                        if (mNavigating) {
1103                            listener.mListener.onGpsStarted();
1104                        } else {
1105                            listener.mListener.onGpsStopped();
1106                        }
1107                    } catch (RemoteException e) {
1108                        Log.w(TAG, "RemoteException in reportStatus");
1109                        mListeners.remove(listener);
1110                        // adjust for size of list changing
1111                        size--;
1112                    }
1113                }
1114
1115                try {
1116                    // update battery stats
1117                    for (int i=mClientUids.size() - 1; i >= 0; i--) {
1118                        int uid = mClientUids.keyAt(i);
1119                        if (mNavigating) {
1120                            mBatteryStats.noteStartGps(uid);
1121                        } else {
1122                            mBatteryStats.noteStopGps(uid);
1123                        }
1124                    }
1125                } catch (RemoteException e) {
1126                    Log.w(TAG, "RemoteException in reportStatus");
1127                }
1128
1129                // send an intent to notify that the GPS has been enabled or disabled.
1130                Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
1131                intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
1132                mContext.sendBroadcast(intent);
1133            }
1134        }
1135    }
1136
1137    /**
1138     * called from native code to update SV info
1139     */
1140    private void reportSvStatus() {
1141
1142        int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
1143
1144        synchronized(mListeners) {
1145            int size = mListeners.size();
1146            for (int i = 0; i < size; i++) {
1147                Listener listener = mListeners.get(i);
1148                try {
1149                    listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
1150                            mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
1151                            mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
1152                } catch (RemoteException e) {
1153                    Log.w(TAG, "RemoteException in reportSvInfo");
1154                    mListeners.remove(listener);
1155                    // adjust for size of list changing
1156                    size--;
1157                }
1158            }
1159        }
1160
1161        if (VERBOSE) {
1162            Log.v(TAG, "SV count: " + svCount +
1163                    " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
1164                    " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
1165            for (int i = 0; i < svCount; i++) {
1166                Log.v(TAG, "sv: " + mSvs[i] +
1167                        " snr: " + (float)mSnrs[i]/10 +
1168                        " elev: " + mSvElevations[i] +
1169                        " azimuth: " + mSvAzimuths[i] +
1170                        ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " E") +
1171                        ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " A") +
1172                        ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
1173            }
1174        }
1175
1176        updateStatus(mStatus, svCount);
1177
1178        if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
1179            System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
1180            // send an intent to notify that the GPS is no longer receiving fixes.
1181            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1182            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
1183            mContext.sendBroadcast(intent);
1184            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
1185        }
1186    }
1187
1188    /**
1189     * called from native code to update AGPS status
1190     */
1191    private void reportAGpsStatus(int type, int status) {
1192        switch (status) {
1193            case GPS_REQUEST_AGPS_DATA_CONN:
1194                 int result = mConnMgr.startUsingNetworkFeature(
1195                        ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1196                if (result == Phone.APN_ALREADY_ACTIVE) {
1197                    if (mAGpsApn != null) {
1198                        native_agps_data_conn_open(mAGpsApn);
1199                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
1200                    } else {
1201                        Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE");
1202                        native_agps_data_conn_failed();
1203                    }
1204                } else if (result == Phone.APN_REQUEST_STARTED) {
1205                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
1206                } else {
1207                    native_agps_data_conn_failed();
1208                }
1209                break;
1210            case GPS_RELEASE_AGPS_DATA_CONN:
1211                if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
1212                    mConnMgr.stopUsingNetworkFeature(
1213                            ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1214                    native_agps_data_conn_closed();
1215                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1216                }
1217                break;
1218            case GPS_AGPS_DATA_CONNECTED:
1219                // Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
1220                break;
1221            case GPS_AGPS_DATA_CONN_DONE:
1222                // Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
1223                break;
1224            case GPS_AGPS_DATA_CONN_FAILED:
1225                // Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
1226                break;
1227        }
1228    }
1229
1230    /**
1231     * called from native code to report NMEA data received
1232     */
1233    private void reportNmea(int index, long timestamp) {
1234        synchronized(mListeners) {
1235            int size = mListeners.size();
1236            if (size > 0) {
1237                // don't bother creating the String if we have no listeners
1238                int length = native_read_nmea(index, mNmeaBuffer, mNmeaBuffer.length);
1239                String nmea = new String(mNmeaBuffer, 0, length);
1240
1241                for (int i = 0; i < size; i++) {
1242                    Listener listener = mListeners.get(i);
1243                    try {
1244                        listener.mListener.onNmeaReceived(timestamp, nmea);
1245                    } catch (RemoteException e) {
1246                        Log.w(TAG, "RemoteException in reportNmea");
1247                        mListeners.remove(listener);
1248                        // adjust for size of list changing
1249                        size--;
1250                    }
1251                }
1252            }
1253        }
1254    }
1255
1256    /**
1257     * called from native code to inform us what the GPS engine capabilities are
1258     */
1259    private void setEngineCapabilities(int capabilities) {
1260        mEngineCapabilities = capabilities;
1261    }
1262
1263    /**
1264     * called from native code to request XTRA data
1265     */
1266    private void xtraDownloadRequest() {
1267        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
1268        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
1269    }
1270
1271    //=============================================================
1272    // NI Client support
1273	//=============================================================
1274    private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
1275    	// Sends a response for an NI reqeust to HAL.
1276    	public boolean sendNiResponse(int notificationId, int userResponse)
1277    	{
1278        	// TODO Add Permission check
1279
1280    		StringBuilder extrasBuf = new StringBuilder();
1281
1282    		if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
1283    				", response: " + userResponse);
1284
1285    		native_send_ni_response(notificationId, userResponse);
1286
1287    		return true;
1288    	}
1289    };
1290
1291    public INetInitiatedListener getNetInitiatedListener() {
1292        return mNetInitiatedListener;
1293    }
1294
1295    // Called by JNI function to report an NI request.
1296	@SuppressWarnings("deprecation")
1297	public void reportNiNotification(
1298        	int notificationId,
1299        	int niType,
1300        	int notifyFlags,
1301        	int timeout,
1302        	int defaultResponse,
1303        	String requestorId,
1304        	String text,
1305        	int requestorIdEncoding,
1306        	int textEncoding,
1307        	String extras  // Encoded extra data
1308        )
1309	{
1310		Log.i(TAG, "reportNiNotification: entered");
1311		Log.i(TAG, "notificationId: " + notificationId +
1312				", niType: " + niType +
1313				", notifyFlags: " + notifyFlags +
1314				", timeout: " + timeout +
1315				", defaultResponse: " + defaultResponse);
1316
1317		Log.i(TAG, "requestorId: " + requestorId +
1318				", text: " + text +
1319				", requestorIdEncoding: " + requestorIdEncoding +
1320				", textEncoding: " + textEncoding);
1321
1322		GpsNiNotification notification = new GpsNiNotification();
1323
1324		notification.notificationId = notificationId;
1325		notification.niType = niType;
1326		notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
1327		notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
1328		notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
1329		notification.timeout = timeout;
1330		notification.defaultResponse = defaultResponse;
1331		notification.requestorId = requestorId;
1332		notification.text = text;
1333		notification.requestorIdEncoding = requestorIdEncoding;
1334		notification.textEncoding = textEncoding;
1335
1336		// Process extras, assuming the format is
1337		// one of more lines of "key = value"
1338		Bundle bundle = new Bundle();
1339
1340		if (extras == null) extras = "";
1341		Properties extraProp = new Properties();
1342
1343		try {
1344			extraProp.load(new StringBufferInputStream(extras));
1345		}
1346		catch (IOException e)
1347		{
1348			Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
1349		}
1350
1351		for (Entry<Object, Object> ent : extraProp.entrySet())
1352		{
1353			bundle.putString((String) ent.getKey(), (String) ent.getValue());
1354		}
1355
1356		notification.extras = bundle;
1357
1358		mNIHandler.handleNiNotification(notification);
1359	}
1360
1361    // this thread is used to receive events from the native code.
1362    // native_wait_for_event() will callback to us via reportLocation(), reportStatus(), etc.
1363    // this is necessary because native code cannot call Java on a thread that the JVM does
1364    // not know about.
1365    private final class GpsEventThread extends Thread {
1366
1367        public GpsEventThread() {
1368            super("GpsEventThread");
1369        }
1370
1371        public void run() {
1372            if (DEBUG) Log.d(TAG, "GpsEventThread starting");
1373            // Exit as soon as disable() is called instead of waiting for the GPS to stop.
1374            while (mEnabled) {
1375                // this will wait for an event from the GPS,
1376                // which will be reported via reportLocation or reportStatus
1377                native_wait_for_event();
1378            }
1379            if (DEBUG) Log.d(TAG, "GpsEventThread exiting");
1380        }
1381    }
1382
1383    private void sendMessage(int message, int arg, Object obj) {
1384        // hold a wake lock while messages are pending
1385        synchronized (mWakeLock) {
1386            mPendingMessageBits |= (1 << message);
1387            mWakeLock.acquire();
1388            mHandler.removeMessages(message);
1389            Message m = Message.obtain(mHandler, message);
1390            m.arg1 = arg;
1391            m.obj = obj;
1392            mHandler.sendMessage(m);
1393        }
1394    }
1395
1396    private final class ProviderHandler extends Handler {
1397        @Override
1398        public void handleMessage(Message msg)
1399        {
1400            int message = msg.what;
1401            switch (message) {
1402                case ENABLE:
1403                    if (msg.arg1 == 1) {
1404                        handleEnable();
1405                    } else {
1406                        handleDisable();
1407                    }
1408                    break;
1409                case ENABLE_TRACKING:
1410                    handleEnableLocationTracking(msg.arg1 == 1);
1411                    break;
1412                case REQUEST_SINGLE_SHOT:
1413                    handleRequestSingleShot();
1414                    break;
1415                case UPDATE_NETWORK_STATE:
1416                    handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
1417                    break;
1418                case INJECT_NTP_TIME:
1419                    handleInjectNtpTime();
1420                    break;
1421                case DOWNLOAD_XTRA_DATA:
1422                    if (native_supports_xtra()) {
1423                        handleDownloadXtraData();
1424                    }
1425                    break;
1426                case UPDATE_LOCATION:
1427                    handleUpdateLocation((Location)msg.obj);
1428                    break;
1429                case ADD_LISTENER:
1430                    handleAddListener(msg.arg1);
1431                    break;
1432                case REMOVE_LISTENER:
1433                    handleRemoveListener(msg.arg1);
1434                    break;
1435            }
1436            // release wake lock if no messages are pending
1437            synchronized (mWakeLock) {
1438                mPendingMessageBits &= ~(1 << message);
1439                if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
1440                    mPendingListenerMessages--;
1441                }
1442                if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
1443                    mWakeLock.release();
1444                }
1445            }
1446        }
1447    };
1448
1449    private final class GpsLocationProviderThread extends Thread {
1450
1451        public GpsLocationProviderThread() {
1452            super("GpsLocationProvider");
1453        }
1454
1455        public void run() {
1456            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1457            initialize();
1458            Looper.prepare();
1459            mHandler = new ProviderHandler();
1460            // signal when we are initialized and ready to go
1461            mInitializedLatch.countDown();
1462            Looper.loop();
1463        }
1464    }
1465
1466    // for GPS SV statistics
1467    private static final int MAX_SVS = 32;
1468    private static final int EPHEMERIS_MASK = 0;
1469    private static final int ALMANAC_MASK = 1;
1470    private static final int USED_FOR_FIX_MASK = 2;
1471
1472    // preallocated arrays, to avoid memory allocation in reportStatus()
1473    private int mSvs[] = new int[MAX_SVS];
1474    private float mSnrs[] = new float[MAX_SVS];
1475    private float mSvElevations[] = new float[MAX_SVS];
1476    private float mSvAzimuths[] = new float[MAX_SVS];
1477    private int mSvMasks[] = new int[3];
1478    private int mSvCount;
1479    // preallocated to avoid memory allocation in reportNmea()
1480    private byte[] mNmeaBuffer = new byte[120];
1481
1482    static { class_init_native(); }
1483    private static native void class_init_native();
1484    private static native boolean native_is_supported();
1485
1486    private native boolean native_init();
1487    private native void native_disable();
1488    private native void native_cleanup();
1489    private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
1490            int preferred_accuracy, int preferred_time);
1491    private native boolean native_start();
1492    private native boolean native_stop();
1493    private native void native_delete_aiding_data(int flags);
1494    private native void native_wait_for_event();
1495    // returns number of SVs
1496    // mask[0] is ephemeris mask and mask[1] is almanac mask
1497    private native int native_read_sv_status(int[] svs, float[] snrs,
1498            float[] elevations, float[] azimuths, int[] masks);
1499    private native int native_read_nmea(int index, byte[] buffer, int bufferSize);
1500    private native void native_inject_location(double latitude, double longitude, float accuracy);
1501
1502    // XTRA Support
1503    private native void native_inject_time(long time, long timeReference, int uncertainty);
1504    private native boolean native_supports_xtra();
1505    private native void native_inject_xtra_data(byte[] data, int length);
1506
1507    // DEBUG Support
1508    private native String native_get_internal_state();
1509
1510    // AGPS Support
1511    private native void native_agps_data_conn_open(String apn);
1512    private native void native_agps_data_conn_closed();
1513    private native void native_agps_data_conn_failed();
1514    private native void native_set_agps_server(int type, String hostname, int port);
1515
1516    // Network-initiated (NI) Support
1517    private native void native_send_ni_response(int notificationId, int userResponse);
1518}
1519