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