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