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