GpsLocationProvider.java revision b711d57ca4e2c6a1befbfa1a41f4b8094755a93f
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    @Override
787    public void switchUser(int userId) {
788        // nothing to do here
789    }
790
791    private void handleSetRequest(ProviderRequest request, WorkSource source) {
792        if (DEBUG) Log.d(TAG, "setRequest " + request);
793
794
795
796        if (request.reportLocation) {
797            // update client uids
798            int[] uids = new int[source.size()];
799            for (int i=0; i < source.size(); i++) {
800                uids[i] = source.get(i);
801            }
802            updateClientUids(uids);
803
804            mFixInterval = (int) request.interval;
805
806            // check for overflow
807            if (mFixInterval != request.interval) {
808                Log.w(TAG, "interval overflow: " + request.interval);
809                mFixInterval = Integer.MAX_VALUE;
810            }
811
812            // apply request to GPS engine
813            if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
814                // change period
815                if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
816                        mFixInterval, 0, 0)) {
817                    Log.e(TAG, "set_position_mode failed in setMinTime()");
818                }
819            } else if (!mStarted) {
820                // start GPS
821                startNavigating();
822            }
823        } else {
824            updateClientUids(new int[0]);
825
826            stopNavigating();
827            mAlarmManager.cancel(mWakeupIntent);
828            mAlarmManager.cancel(mTimeoutIntent);
829        }
830    }
831
832    private final class Listener implements IBinder.DeathRecipient {
833        final IGpsStatusListener mListener;
834
835        Listener(IGpsStatusListener listener) {
836            mListener = listener;
837        }
838
839        @Override
840        public void binderDied() {
841            if (DEBUG) Log.d(TAG, "GPS status listener died");
842
843            synchronized (mListeners) {
844                mListeners.remove(this);
845            }
846            if (mListener != null) {
847                mListener.asBinder().unlinkToDeath(this, 0);
848            }
849        }
850    }
851
852    private void updateClientUids(int[] uids) {
853        // Find uid's that were not previously tracked
854        for (int uid1 : uids) {
855            boolean newUid = true;
856            for (int uid2 : mClientUids) {
857                if (uid1 == uid2) {
858                    newUid = false;
859                    break;
860                }
861            }
862            if (newUid) {
863                try {
864                    mBatteryStats.noteStartGps(uid1);
865                } catch (RemoteException e) {
866                    Log.w(TAG, "RemoteException", e);
867                }
868            }
869        }
870
871        // Find uid'd that were tracked but have now disappeared
872        for (int uid1 : mClientUids) {
873            boolean oldUid = true;
874            for (int uid2 : uids) {
875                if (uid1 == uid2) {
876                    oldUid = false;
877                    break;
878                }
879            }
880            if (oldUid) {
881                try {
882                    mBatteryStats.noteStopGps(uid1);
883                } catch (RemoteException e) {
884                    Log.w(TAG, "RemoteException", e);
885                }
886            }
887        }
888
889        // save current uids
890        mClientUids = uids;
891    }
892
893    @Override
894    public boolean sendExtraCommand(String command, Bundle extras) {
895
896        long identity = Binder.clearCallingIdentity();
897        boolean result = false;
898
899        if ("delete_aiding_data".equals(command)) {
900            result = deleteAidingData(extras);
901        } else if ("force_time_injection".equals(command)) {
902            sendMessage(INJECT_NTP_TIME, 0, null);
903            result = true;
904        } else if ("force_xtra_injection".equals(command)) {
905            if (mSupportsXtra) {
906                xtraDownloadRequest();
907                result = true;
908            }
909        } else {
910            Log.w(TAG, "sendExtraCommand: unknown command " + command);
911        }
912
913        Binder.restoreCallingIdentity(identity);
914        return result;
915    }
916
917    private boolean deleteAidingData(Bundle extras) {
918        int flags;
919
920        if (extras == null) {
921            flags = GPS_DELETE_ALL;
922        } else {
923            flags = 0;
924            if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
925            if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
926            if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
927            if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
928            if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
929            if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
930            if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
931            if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
932            if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
933            if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
934            if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
935            if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
936            if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
937        }
938
939        if (flags != 0) {
940            native_delete_aiding_data(flags);
941            return true;
942        }
943
944        return false;
945    }
946
947    private void startNavigating() {
948        if (!mStarted) {
949            if (DEBUG) Log.d(TAG, "startNavigating");
950            mTimeToFirstFix = 0;
951            mLastFixTime = 0;
952            mStarted = true;
953            mPositionMode = GPS_POSITION_MODE_STANDALONE;
954
955             if (Settings.Global.getInt(mContext.getContentResolver(),
956                    Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0) {
957                if (hasCapability(GPS_CAPABILITY_MSB)) {
958                    mPositionMode = GPS_POSITION_MODE_MS_BASED;
959                }
960            }
961
962            int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
963            if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
964                    interval, 0, 0)) {
965                mStarted = false;
966                Log.e(TAG, "set_position_mode failed in startNavigating()");
967                return;
968            }
969            if (!native_start()) {
970                mStarted = false;
971                Log.e(TAG, "native_start failed in startNavigating()");
972                return;
973            }
974
975            // reset SV count to zero
976            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
977            mFixRequestTime = System.currentTimeMillis();
978            if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
979                // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
980                // and our fix interval is not short
981                if (mFixInterval >= NO_FIX_TIMEOUT) {
982                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
983                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
984                }
985            }
986        }
987    }
988
989    private void stopNavigating() {
990        if (DEBUG) Log.d(TAG, "stopNavigating");
991        if (mStarted) {
992            mStarted = false;
993            native_stop();
994            mTimeToFirstFix = 0;
995            mLastFixTime = 0;
996            mLocationFlags = LOCATION_INVALID;
997
998            // reset SV count to zero
999            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
1000        }
1001    }
1002
1003    private void hibernate() {
1004        // stop GPS until our next fix interval arrives
1005        stopNavigating();
1006        mAlarmManager.cancel(mTimeoutIntent);
1007        mAlarmManager.cancel(mWakeupIntent);
1008        long now = SystemClock.elapsedRealtime();
1009        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent);
1010    }
1011
1012    private boolean hasCapability(int capability) {
1013        return ((mEngineCapabilities & capability) != 0);
1014    }
1015
1016    /**
1017     * called from native code to update our position.
1018     */
1019    private void reportLocation(int flags, double latitude, double longitude, double altitude,
1020            float speed, float bearing, float accuracy, long timestamp) {
1021        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
1022                " timestamp: " + timestamp);
1023
1024        synchronized (mLocation) {
1025            mLocationFlags = flags;
1026            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1027                mLocation.setLatitude(latitude);
1028                mLocation.setLongitude(longitude);
1029                mLocation.setTime(timestamp);
1030                // It would be nice to push the elapsed real-time timestamp
1031                // further down the stack, but this is still useful
1032                mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1033            }
1034            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1035                mLocation.setAltitude(altitude);
1036            } else {
1037                mLocation.removeAltitude();
1038            }
1039            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1040                mLocation.setSpeed(speed);
1041            } else {
1042                mLocation.removeSpeed();
1043            }
1044            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1045                mLocation.setBearing(bearing);
1046            } else {
1047                mLocation.removeBearing();
1048            }
1049            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1050                mLocation.setAccuracy(accuracy);
1051            } else {
1052                mLocation.removeAccuracy();
1053            }
1054            mLocation.setExtras(mLocationExtras);
1055
1056            try {
1057                mILocationManager.reportLocation(mLocation, false);
1058            } catch (RemoteException e) {
1059                Log.e(TAG, "RemoteException calling reportLocation");
1060            }
1061        }
1062
1063        mLastFixTime = System.currentTimeMillis();
1064        // report time to first fix
1065        if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1066            mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
1067            if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
1068
1069            // notify status listeners
1070            synchronized (mListeners) {
1071                int size = mListeners.size();
1072                for (int i = 0; i < size; i++) {
1073                    Listener listener = mListeners.get(i);
1074                    try {
1075                        listener.mListener.onFirstFix(mTimeToFirstFix);
1076                    } catch (RemoteException e) {
1077                        Log.w(TAG, "RemoteException in stopNavigating");
1078                        mListeners.remove(listener);
1079                        // adjust for size of list changing
1080                        size--;
1081                    }
1082                }
1083            }
1084        }
1085
1086        if (mStarted && mStatus != LocationProvider.AVAILABLE) {
1087            // we want to time out if we do not receive a fix
1088            // within the time out and we are requesting infrequent fixes
1089            if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
1090                mAlarmManager.cancel(mTimeoutIntent);
1091            }
1092
1093            // send an intent to notify that the GPS is receiving fixes.
1094            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1095            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
1096            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1097            updateStatus(LocationProvider.AVAILABLE, mSvCount);
1098        }
1099
1100       if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
1101               mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
1102            if (DEBUG) Log.d(TAG, "got fix, hibernating");
1103            hibernate();
1104        }
1105   }
1106
1107    /**
1108     * called from native code to update our status
1109     */
1110    private void reportStatus(int status) {
1111        if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
1112
1113        synchronized (mListeners) {
1114            boolean wasNavigating = mNavigating;
1115
1116            switch (status) {
1117                case GPS_STATUS_SESSION_BEGIN:
1118                    mNavigating = true;
1119                    mEngineOn = true;
1120                    break;
1121                case GPS_STATUS_SESSION_END:
1122                    mNavigating = false;
1123                    break;
1124                case GPS_STATUS_ENGINE_ON:
1125                    mEngineOn = true;
1126                    break;
1127                case GPS_STATUS_ENGINE_OFF:
1128                    mEngineOn = false;
1129                    mNavigating = false;
1130                    break;
1131            }
1132
1133            if (wasNavigating != mNavigating) {
1134                int size = mListeners.size();
1135                for (int i = 0; i < size; i++) {
1136                    Listener listener = mListeners.get(i);
1137                    try {
1138                        if (mNavigating) {
1139                            listener.mListener.onGpsStarted();
1140                        } else {
1141                            listener.mListener.onGpsStopped();
1142                        }
1143                    } catch (RemoteException e) {
1144                        Log.w(TAG, "RemoteException in reportStatus");
1145                        mListeners.remove(listener);
1146                        // adjust for size of list changing
1147                        size--;
1148                    }
1149                }
1150
1151                // send an intent to notify that the GPS has been enabled or disabled.
1152                Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
1153                intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
1154                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1155            }
1156        }
1157    }
1158
1159    /**
1160     * called from native code to update SV info
1161     */
1162    private void reportSvStatus() {
1163
1164        int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
1165
1166        synchronized (mListeners) {
1167            int size = mListeners.size();
1168            for (int i = 0; i < size; i++) {
1169                Listener listener = mListeners.get(i);
1170                try {
1171                    listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
1172                            mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
1173                            mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
1174                } catch (RemoteException e) {
1175                    Log.w(TAG, "RemoteException in reportSvInfo");
1176                    mListeners.remove(listener);
1177                    // adjust for size of list changing
1178                    size--;
1179                }
1180            }
1181        }
1182
1183        if (VERBOSE) {
1184            Log.v(TAG, "SV count: " + svCount +
1185                    " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
1186                    " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
1187            for (int i = 0; i < svCount; i++) {
1188                Log.v(TAG, "sv: " + mSvs[i] +
1189                        " snr: " + mSnrs[i]/10 +
1190                        " elev: " + mSvElevations[i] +
1191                        " azimuth: " + mSvAzimuths[i] +
1192                        ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " E") +
1193                        ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " A") +
1194                        ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
1195            }
1196        }
1197
1198        // return number of sets used in fix instead of total
1199        updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
1200
1201        if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
1202            System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
1203            // send an intent to notify that the GPS is no longer receiving fixes.
1204            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1205            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
1206            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1207            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
1208        }
1209    }
1210
1211    /**
1212     * called from native code to update AGPS status
1213     */
1214    private void reportAGpsStatus(int type, int status, int ipaddr) {
1215        switch (status) {
1216            case GPS_REQUEST_AGPS_DATA_CONN:
1217                if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
1218                // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
1219                //  to avoid a race condition with handleUpdateNetworkState()
1220                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
1221                int result = mConnMgr.startUsingNetworkFeature(
1222                        ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1223                mAGpsDataConnectionIpAddr = ipaddr;
1224                if (result == PhoneConstants.APN_ALREADY_ACTIVE) {
1225                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE");
1226                    if (mAGpsApn != null) {
1227                        Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr);
1228                        if (mAGpsDataConnectionIpAddr != 0xffffffff) {
1229                            boolean route_result;
1230                            if (DEBUG) Log.d(TAG, "call requestRouteToHost");
1231                            route_result = mConnMgr.requestRouteToHost(
1232                                ConnectivityManager.TYPE_MOBILE_SUPL,
1233                                mAGpsDataConnectionIpAddr);
1234                            if (route_result == false) Log.d(TAG, "call requestRouteToHost failed");
1235                        }
1236                        native_agps_data_conn_open(mAGpsApn);
1237                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
1238                    } else {
1239                        Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE");
1240                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1241                        native_agps_data_conn_failed();
1242                    }
1243                } else if (result == PhoneConstants.APN_REQUEST_STARTED) {
1244                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_REQUEST_STARTED");
1245                    // Nothing to do here
1246                } else {
1247                    if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed");
1248                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1249                    native_agps_data_conn_failed();
1250                }
1251                break;
1252            case GPS_RELEASE_AGPS_DATA_CONN:
1253                if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
1254                if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
1255                    mConnMgr.stopUsingNetworkFeature(
1256                            ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1257                    native_agps_data_conn_closed();
1258                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1259                }
1260                break;
1261            case GPS_AGPS_DATA_CONNECTED:
1262                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
1263                break;
1264            case GPS_AGPS_DATA_CONN_DONE:
1265                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
1266                break;
1267            case GPS_AGPS_DATA_CONN_FAILED:
1268                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
1269                break;
1270        }
1271    }
1272
1273    /**
1274     * called from native code to report NMEA data received
1275     */
1276    private void reportNmea(long timestamp) {
1277        synchronized (mListeners) {
1278            int size = mListeners.size();
1279            if (size > 0) {
1280                // don't bother creating the String if we have no listeners
1281                int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
1282                String nmea = new String(mNmeaBuffer, 0, length);
1283
1284                for (int i = 0; i < size; i++) {
1285                    Listener listener = mListeners.get(i);
1286                    try {
1287                        listener.mListener.onNmeaReceived(timestamp, nmea);
1288                    } catch (RemoteException e) {
1289                        Log.w(TAG, "RemoteException in reportNmea");
1290                        mListeners.remove(listener);
1291                        // adjust for size of list changing
1292                        size--;
1293                    }
1294                }
1295            }
1296        }
1297    }
1298
1299    /**
1300     * called from native code to inform us what the GPS engine capabilities are
1301     */
1302    private void setEngineCapabilities(int capabilities) {
1303        mEngineCapabilities = capabilities;
1304
1305        if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
1306            mPeriodicTimeInjection = true;
1307            requestUtcTime();
1308        }
1309    }
1310
1311    /**
1312     * called from native code to request XTRA data
1313     */
1314    private void xtraDownloadRequest() {
1315        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
1316        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
1317    }
1318
1319    //=============================================================
1320    // NI Client support
1321    //=============================================================
1322    private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
1323        // Sends a response for an NI reqeust to HAL.
1324        @Override
1325        public boolean sendNiResponse(int notificationId, int userResponse)
1326        {
1327            // TODO Add Permission check
1328
1329            if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
1330                    ", response: " + userResponse);
1331            native_send_ni_response(notificationId, userResponse);
1332            return true;
1333        }
1334    };
1335
1336    public INetInitiatedListener getNetInitiatedListener() {
1337        return mNetInitiatedListener;
1338    }
1339
1340    // Called by JNI function to report an NI request.
1341    public void reportNiNotification(
1342            int notificationId,
1343            int niType,
1344            int notifyFlags,
1345            int timeout,
1346            int defaultResponse,
1347            String requestorId,
1348            String text,
1349            int requestorIdEncoding,
1350            int textEncoding,
1351            String extras  // Encoded extra data
1352        )
1353    {
1354        Log.i(TAG, "reportNiNotification: entered");
1355        Log.i(TAG, "notificationId: " + notificationId +
1356                ", niType: " + niType +
1357                ", notifyFlags: " + notifyFlags +
1358                ", timeout: " + timeout +
1359                ", defaultResponse: " + defaultResponse);
1360
1361        Log.i(TAG, "requestorId: " + requestorId +
1362                ", text: " + text +
1363                ", requestorIdEncoding: " + requestorIdEncoding +
1364                ", textEncoding: " + textEncoding);
1365
1366        GpsNiNotification notification = new GpsNiNotification();
1367
1368        notification.notificationId = notificationId;
1369        notification.niType = niType;
1370        notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
1371        notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
1372        notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
1373        notification.timeout = timeout;
1374        notification.defaultResponse = defaultResponse;
1375        notification.requestorId = requestorId;
1376        notification.text = text;
1377        notification.requestorIdEncoding = requestorIdEncoding;
1378        notification.textEncoding = textEncoding;
1379
1380        // Process extras, assuming the format is
1381        // one of more lines of "key = value"
1382        Bundle bundle = new Bundle();
1383
1384        if (extras == null) extras = "";
1385        Properties extraProp = new Properties();
1386
1387        try {
1388            extraProp.load(new StringReader(extras));
1389        }
1390        catch (IOException e)
1391        {
1392            Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
1393        }
1394
1395        for (Entry<Object, Object> ent : extraProp.entrySet())
1396        {
1397            bundle.putString((String) ent.getKey(), (String) ent.getValue());
1398        }
1399
1400        notification.extras = bundle;
1401
1402        mNIHandler.handleNiNotification(notification);
1403    }
1404
1405    /**
1406     * Called from native code to request set id info.
1407     * We should be careful about receiving null string from the TelephonyManager,
1408     * because sending null String to JNI function would cause a crash.
1409     */
1410
1411    private void requestSetID(int flags) {
1412        TelephonyManager phone = (TelephonyManager)
1413                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1414        int    type = AGPS_SETID_TYPE_NONE;
1415        String data = "";
1416
1417        if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
1418            String data_temp = phone.getSubscriberId();
1419            if (data_temp == null) {
1420                // This means the framework does not have the SIM card ready.
1421            } else {
1422                // This means the framework has the SIM card.
1423                data = data_temp;
1424                type = AGPS_SETID_TYPE_IMSI;
1425            }
1426        }
1427        else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
1428            String data_temp = phone.getLine1Number();
1429            if (data_temp == null) {
1430                // This means the framework does not have the SIM card ready.
1431            } else {
1432                // This means the framework has the SIM card.
1433                data = data_temp;
1434                type = AGPS_SETID_TYPE_MSISDN;
1435            }
1436        }
1437        native_agps_set_id(type, data);
1438    }
1439
1440    /**
1441     * Called from native code to request utc time info
1442     */
1443
1444    private void requestUtcTime() {
1445        sendMessage(INJECT_NTP_TIME, 0, null);
1446    }
1447
1448    /**
1449     * Called from native code to request reference location info
1450     */
1451
1452    private void requestRefLocation(int flags) {
1453        TelephonyManager phone = (TelephonyManager)
1454                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1455        if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
1456            GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
1457            if ((gsm_cell != null) && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) &&
1458                    (phone.getNetworkOperator() != null) &&
1459                        (phone.getNetworkOperator().length() > 3)) {
1460                int type;
1461                int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
1462                int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
1463                int networkType = phone.getNetworkType();
1464                if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
1465                    || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
1466                    || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
1467                    || networkType == TelephonyManager.NETWORK_TYPE_HSPA) {
1468                    type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
1469                } else {
1470                    type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
1471                }
1472                native_agps_set_ref_location_cellid(type, mcc, mnc,
1473                        gsm_cell.getLac(), gsm_cell.getCid());
1474            } else {
1475                Log.e(TAG,"Error getting cell location info.");
1476            }
1477        }
1478        else {
1479            Log.e(TAG,"CDMA not supported.");
1480        }
1481    }
1482
1483    private void sendMessage(int message, int arg, Object obj) {
1484        // hold a wake lock until this message is delivered
1485        // note that this assumes the message will not be removed from the queue before
1486        // it is handled (otherwise the wake lock would be leaked).
1487        mWakeLock.acquire();
1488        mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
1489    }
1490
1491    private final class ProviderHandler extends Handler {
1492        public ProviderHandler() {
1493            super(true /*async*/);
1494        }
1495
1496        @Override
1497        public void handleMessage(Message msg) {
1498            int message = msg.what;
1499            switch (message) {
1500                case ENABLE:
1501                    if (msg.arg1 == 1) {
1502                        handleEnable();
1503                    } else {
1504                        handleDisable();
1505                    }
1506                    break;
1507                case SET_REQUEST:
1508                    GpsRequest gpsRequest = (GpsRequest) msg.obj;
1509                    handleSetRequest(gpsRequest.request, gpsRequest.source);
1510                    break;
1511                case UPDATE_NETWORK_STATE:
1512                    handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
1513                    break;
1514                case INJECT_NTP_TIME:
1515                    handleInjectNtpTime();
1516                    break;
1517                case DOWNLOAD_XTRA_DATA:
1518                    if (mSupportsXtra) {
1519                        handleDownloadXtraData();
1520                    }
1521                    break;
1522                case INJECT_NTP_TIME_FINISHED:
1523                    mInjectNtpTimePending = STATE_IDLE;
1524                    break;
1525                case DOWNLOAD_XTRA_DATA_FINISHED:
1526                    mDownloadXtraDataPending = STATE_IDLE;
1527                    break;
1528                case UPDATE_LOCATION:
1529                    handleUpdateLocation((Location)msg.obj);
1530                    break;
1531            }
1532            if (msg.arg2 == 1) {
1533                // wakelock was taken for this message, release it
1534                mWakeLock.release();
1535            }
1536        }
1537    };
1538
1539    private final class NetworkLocationListener implements LocationListener {
1540        @Override
1541        public void onLocationChanged(Location location) {
1542            // this callback happens on mHandler looper
1543            if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
1544                handleUpdateLocation(location);
1545            }
1546        }
1547        @Override
1548        public void onStatusChanged(String provider, int status, Bundle extras) { }
1549        @Override
1550        public void onProviderEnabled(String provider) { }
1551        @Override
1552        public void onProviderDisabled(String provider) { }
1553    }
1554
1555    private String getSelectedApn() {
1556        Uri uri = Uri.parse("content://telephony/carriers/preferapn");
1557        String apn = null;
1558
1559        Cursor cursor = mContext.getContentResolver().query(uri, new String[] {"apn"},
1560                null, null, Carriers.DEFAULT_SORT_ORDER);
1561
1562        if (null != cursor) {
1563            try {
1564                if (cursor.moveToFirst()) {
1565                    apn = cursor.getString(0);
1566                }
1567            } finally {
1568                cursor.close();
1569            }
1570        }
1571        return apn;
1572    }
1573
1574    @Override
1575    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1576        StringBuilder s = new StringBuilder();
1577        s.append("  mFixInterval=").append(mFixInterval).append("\n");
1578        s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
1579        if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
1580        if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
1581        if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
1582        if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
1583        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
1584        s.append(")\n");
1585
1586        s.append(native_get_internal_state());
1587        pw.append(s);
1588    }
1589
1590    // for GPS SV statistics
1591    private static final int MAX_SVS = 32;
1592    private static final int EPHEMERIS_MASK = 0;
1593    private static final int ALMANAC_MASK = 1;
1594    private static final int USED_FOR_FIX_MASK = 2;
1595
1596    // preallocated arrays, to avoid memory allocation in reportStatus()
1597    private int mSvs[] = new int[MAX_SVS];
1598    private float mSnrs[] = new float[MAX_SVS];
1599    private float mSvElevations[] = new float[MAX_SVS];
1600    private float mSvAzimuths[] = new float[MAX_SVS];
1601    private int mSvMasks[] = new int[3];
1602    private int mSvCount;
1603    // preallocated to avoid memory allocation in reportNmea()
1604    private byte[] mNmeaBuffer = new byte[120];
1605
1606    static { class_init_native(); }
1607    private static native void class_init_native();
1608    private static native boolean native_is_supported();
1609
1610    private native boolean native_init();
1611    private native void native_cleanup();
1612    private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
1613            int preferred_accuracy, int preferred_time);
1614    private native boolean native_start();
1615    private native boolean native_stop();
1616    private native void native_delete_aiding_data(int flags);
1617    // returns number of SVs
1618    // mask[0] is ephemeris mask and mask[1] is almanac mask
1619    private native int native_read_sv_status(int[] svs, float[] snrs,
1620            float[] elevations, float[] azimuths, int[] masks);
1621    private native int native_read_nmea(byte[] buffer, int bufferSize);
1622    private native void native_inject_location(double latitude, double longitude, float accuracy);
1623
1624    // XTRA Support
1625    private native void native_inject_time(long time, long timeReference, int uncertainty);
1626    private native boolean native_supports_xtra();
1627    private native void native_inject_xtra_data(byte[] data, int length);
1628
1629    // DEBUG Support
1630    private native String native_get_internal_state();
1631
1632    // AGPS Support
1633    private native void native_agps_data_conn_open(String apn);
1634    private native void native_agps_data_conn_closed();
1635    private native void native_agps_data_conn_failed();
1636    private native void native_agps_ni_message(byte [] msg, int length);
1637    private native void native_set_agps_server(int type, String hostname, int port);
1638
1639    // Network-initiated (NI) Support
1640    private native void native_send_ni_response(int notificationId, int userResponse);
1641
1642    // AGPS ril suport
1643    private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
1644            int lac, int cid);
1645    private native void native_agps_set_id(int type, String setid);
1646
1647    private native void native_update_network_state(boolean connected, int type,
1648            boolean roaming, boolean available, String extraInfo, String defaultAPN);
1649}
1650