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