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