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