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