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