GpsLocationProvider.java revision 5ac72a29593ab9a20337a2225df52bdf4754be02
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.location;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.database.Cursor;
26import android.location.Criteria;
27import android.location.IGpsStatusListener;
28import android.location.IGpsStatusProvider;
29import android.location.ILocationManager;
30import android.location.INetInitiatedListener;
31import android.location.Location;
32import android.location.LocationListener;
33import android.location.LocationManager;
34import android.location.LocationProvider;
35import android.net.ConnectivityManager;
36import android.net.NetworkInfo;
37import android.net.Uri;
38import android.os.AsyncTask;
39import android.os.Binder;
40import android.os.Bundle;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.Looper;
44import android.os.Message;
45import android.os.PowerManager;
46import android.os.Process;
47import android.os.RemoteException;
48import android.os.ServiceManager;
49import android.os.SystemClock;
50import android.os.UserHandle;
51import android.os.WorkSource;
52import android.provider.Settings;
53import android.provider.Telephony.Carriers;
54import android.provider.Telephony.Sms.Intents;
55import android.telephony.SmsMessage;
56import android.telephony.TelephonyManager;
57import android.telephony.gsm.GsmCellLocation;
58import android.util.Log;
59import android.util.NtpTrustedTime;
60import com.android.internal.app.IBatteryStats;
61import com.android.internal.location.GpsNetInitiatedHandler;
62import com.android.internal.location.ProviderProperties;
63import com.android.internal.location.ProviderRequest;
64import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
65import com.android.internal.telephony.Phone;
66import com.android.internal.telephony.PhoneConstants;
67
68import java.io.File;
69import java.io.FileDescriptor;
70import java.io.FileInputStream;
71import java.io.IOException;
72import java.io.PrintWriter;
73import java.io.StringReader;
74import java.util.ArrayList;
75import java.util.Date;
76import java.util.Map.Entry;
77import java.util.Properties;
78import java.util.concurrent.CountDownLatch;
79
80/**
81 * A GPS implementation of LocationProvider used by LocationManager.
82 *
83 * {@hide}
84 */
85public class GpsLocationProvider implements LocationProviderInterface {
86
87    private static final String TAG = "GpsLocationProvider";
88
89    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
90    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
91
92    private static final ProviderProperties PROPERTIES = new ProviderProperties(
93            true, true, false, false, true, true, true,
94            Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
95
96    // these need to match GpsPositionMode enum in gps.h
97    private static final int GPS_POSITION_MODE_STANDALONE = 0;
98    private static final int GPS_POSITION_MODE_MS_BASED = 1;
99    private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
100
101    // these need to match GpsPositionRecurrence enum in gps.h
102    private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
103    private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
104
105    // these need to match GpsStatusValue defines in gps.h
106    private static final int GPS_STATUS_NONE = 0;
107    private static final int GPS_STATUS_SESSION_BEGIN = 1;
108    private static final int GPS_STATUS_SESSION_END = 2;
109    private static final int GPS_STATUS_ENGINE_ON = 3;
110    private static final int GPS_STATUS_ENGINE_OFF = 4;
111
112    // these need to match GpsApgsStatusValue defines in gps.h
113    /** AGPS status event values. */
114    private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
115    private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
116    private static final int GPS_AGPS_DATA_CONNECTED = 3;
117    private static final int GPS_AGPS_DATA_CONN_DONE = 4;
118    private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
119
120    // these need to match GpsLocationFlags enum in gps.h
121    private static final int LOCATION_INVALID = 0;
122    private static final int LOCATION_HAS_LAT_LONG = 1;
123    private static final int LOCATION_HAS_ALTITUDE = 2;
124    private static final int LOCATION_HAS_SPEED = 4;
125    private static final int LOCATION_HAS_BEARING = 8;
126    private static final int LOCATION_HAS_ACCURACY = 16;
127
128// IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
129    private static final int GPS_DELETE_EPHEMERIS = 0x0001;
130    private static final int GPS_DELETE_ALMANAC = 0x0002;
131    private static final int GPS_DELETE_POSITION = 0x0004;
132    private static final int GPS_DELETE_TIME = 0x0008;
133    private static final int GPS_DELETE_IONO = 0x0010;
134    private static final int GPS_DELETE_UTC = 0x0020;
135    private static final int GPS_DELETE_HEALTH = 0x0040;
136    private static final int GPS_DELETE_SVDIR = 0x0080;
137    private static final int GPS_DELETE_SVSTEER = 0x0100;
138    private static final int GPS_DELETE_SADATA = 0x0200;
139    private static final int GPS_DELETE_RTI = 0x0400;
140    private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
141    private static final int GPS_DELETE_ALL = 0xFFFF;
142
143    // The GPS_CAPABILITY_* flags must match the values in gps.h
144    private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
145    private static final int GPS_CAPABILITY_MSB = 0x0000002;
146    private static final int GPS_CAPABILITY_MSA = 0x0000004;
147    private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
148    private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
149
150    // these need to match AGpsType enum in gps.h
151    private static final int AGPS_TYPE_SUPL = 1;
152    private static final int AGPS_TYPE_C2K = 2;
153
154    // for mAGpsDataConnectionState
155    private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
156    private static final int AGPS_DATA_CONNECTION_OPENING = 1;
157    private static final int AGPS_DATA_CONNECTION_OPEN = 2;
158
159    // Handler messages
160    private static final int CHECK_LOCATION = 1;
161    private static final int ENABLE = 2;
162    private static final int SET_REQUEST = 3;
163    private static final int UPDATE_NETWORK_STATE = 4;
164    private static final int INJECT_NTP_TIME = 5;
165    private static final int DOWNLOAD_XTRA_DATA = 6;
166    private static final int UPDATE_LOCATION = 7;
167    private static final int ADD_LISTENER = 8;
168    private static final int REMOVE_LISTENER = 9;
169    private static final int INJECT_NTP_TIME_FINISHED = 10;
170    private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
171
172    // Request setid
173    private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
174    private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
175
176    // Request ref location
177    private static final int AGPS_RIL_REQUEST_REFLOC_CELLID = 1;
178    private static final int AGPS_RIL_REQUEST_REFLOC_MAC = 2;
179
180    // ref. location info
181    private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
182    private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
183    private static final int AGPS_REG_LOCATION_TYPE_MAC        = 3;
184
185    // set id info
186    private static final int AGPS_SETID_TYPE_NONE = 0;
187    private static final int AGPS_SETID_TYPE_IMSI = 1;
188    private static final int AGPS_SETID_TYPE_MSISDN = 2;
189
190    private static final String PROPERTIES_FILE = "/etc/gps.conf";
191
192    /** simpler wrapper for ProviderRequest + Worksource */
193    private static class GpsRequest {
194        public ProviderRequest request;
195        public WorkSource source;
196        public GpsRequest(ProviderRequest request, WorkSource source) {
197            this.request = request;
198            this.source = source;
199        }
200    }
201
202    private Object mLock = new Object();
203
204    private int mLocationFlags = LOCATION_INVALID;
205
206    // current status
207    private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
208
209    // time for last status update
210    private long mStatusUpdateTime = SystemClock.elapsedRealtime();
211
212    // turn off GPS fix icon if we haven't received a fix in 10 seconds
213    private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
214
215    // stop trying if we do not receive a fix within 60 seconds
216    private static final int NO_FIX_TIMEOUT = 60 * 1000;
217
218    // if the fix interval is below this we leave GPS on,
219    // if above then we cycle the GPS driver.
220    // Typical hot TTTF is ~5 seconds, so 10 seconds seems sane.
221    private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
222
223    // how often to request NTP time, in milliseconds
224    // current setting 24 hours
225    private static final long NTP_INTERVAL = 24*60*60*1000;
226    // how long to wait if we have a network error in NTP or XTRA downloading
227    // current setting - 5 minutes
228    private static final long RETRY_INTERVAL = 5*60*1000;
229
230    // true if we are enabled, protected by this
231    private boolean mEnabled;
232
233    // true if we have network connectivity
234    private boolean mNetworkAvailable;
235
236    // states for injecting ntp and downloading xtra data
237    private static final int STATE_PENDING_NETWORK = 0;
238    private static final int STATE_DOWNLOADING = 1;
239    private static final int STATE_IDLE = 2;
240
241    // flags to trigger NTP or XTRA data download when network becomes available
242    // initialized to true so we do NTP and XTRA when the network comes up after booting
243    private int mInjectNtpTimePending = STATE_PENDING_NETWORK;
244    private int mDownloadXtraDataPending = STATE_PENDING_NETWORK;
245
246    // set to true if the GPS engine does not do on-demand NTP time requests
247    private boolean mPeriodicTimeInjection;
248
249    // true if GPS is navigating
250    private boolean mNavigating;
251
252    // true if GPS engine is on
253    private boolean mEngineOn;
254
255    // requested frequency of fixes, in milliseconds
256    private int mFixInterval = 1000;
257
258    // true if we started navigation
259    private boolean mStarted;
260
261    // capabilities of the GPS engine
262    private int mEngineCapabilities;
263
264    // true if XTRA is supported
265    private boolean mSupportsXtra;
266
267    // for calculating time to first fix
268    private long mFixRequestTime = 0;
269    // time to first fix for most recent session
270    private int mTimeToFirstFix = 0;
271    // time we received our last fix
272    private long mLastFixTime;
273
274    private int mPositionMode;
275
276    // properties loaded from PROPERTIES_FILE
277    private Properties mProperties;
278    private String mSuplServerHost;
279    private int mSuplServerPort;
280    private String mC2KServerHost;
281    private int mC2KServerPort;
282
283    private final Context mContext;
284    private final NtpTrustedTime mNtpTime;
285    private final ILocationManager mILocationManager;
286    private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
287    private Bundle mLocationExtras = new Bundle();
288    private ArrayList<Listener> mListeners = new ArrayList<Listener>();
289
290    // GpsLocationProvider's handler thread
291    private final Thread mThread;
292    // Handler for processing events in mThread.
293    private Handler mHandler;
294    // Used to signal when our main thread has initialized everything
295    private final CountDownLatch mInitializedLatch = new CountDownLatch(1);
296
297    private String mAGpsApn;
298    private int mAGpsDataConnectionState;
299    private int mAGpsDataConnectionIpAddr;
300    private final ConnectivityManager mConnMgr;
301    private final GpsNetInitiatedHandler mNIHandler;
302
303    // Wakelocks
304    private final static String WAKELOCK_KEY = "GpsLocationProvider";
305    private final PowerManager.WakeLock mWakeLock;
306
307    // Alarms
308    private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
309    private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
310    private final AlarmManager mAlarmManager;
311    private final PendingIntent mWakeupIntent;
312    private final PendingIntent mTimeoutIntent;
313
314    private final IBatteryStats mBatteryStats;
315
316    // only modified on handler thread
317    private int[] mClientUids = new int[0];
318
319    private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
320        @Override
321        public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
322            if (listener == null) {
323                throw new NullPointerException("listener is null in addGpsStatusListener");
324            }
325
326            synchronized (mListeners) {
327                IBinder binder = listener.asBinder();
328                int size = mListeners.size();
329                for (int i = 0; i < size; i++) {
330                    Listener test = mListeners.get(i);
331                    if (binder.equals(test.mListener.asBinder())) {
332                        // listener already added
333                        return;
334                    }
335                }
336
337                Listener l = new Listener(listener);
338                binder.linkToDeath(l, 0);
339                mListeners.add(l);
340            }
341        }
342
343        @Override
344        public void removeGpsStatusListener(IGpsStatusListener listener) {
345            if (listener == null) {
346                throw new NullPointerException("listener is null in addGpsStatusListener");
347            }
348
349            synchronized (mListeners) {
350                IBinder binder = listener.asBinder();
351                Listener l = null;
352                int size = mListeners.size();
353                for (int i = 0; i < size && l == null; i++) {
354                    Listener test = mListeners.get(i);
355                    if (binder.equals(test.mListener.asBinder())) {
356                        l = test;
357                    }
358                }
359
360                if (l != null) {
361                    mListeners.remove(l);
362                    binder.unlinkToDeath(l, 0);
363                }
364            }
365        }
366    };
367
368    public IGpsStatusProvider getGpsStatusProvider() {
369        return mGpsStatusProvider;
370    }
371
372    private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
373        @Override public void onReceive(Context context, Intent intent) {
374            String action = intent.getAction();
375
376            if (action.equals(ALARM_WAKEUP)) {
377                if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
378                startNavigating();
379            } else if (action.equals(ALARM_TIMEOUT)) {
380                if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
381                hibernate();
382            } else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
383                checkSmsSuplInit(intent);
384            } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
385                checkWapSuplInit(intent);
386             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
387                 int networkState;
388                 if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
389                     networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
390                 } else {
391                     networkState = LocationProvider.AVAILABLE;
392                 }
393
394                 // retrieve NetworkInfo result for this UID
395                 NetworkInfo info =
396                         intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
397                 ConnectivityManager connManager = (ConnectivityManager)
398                         mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
399                 info = connManager.getNetworkInfo(info.getType());
400
401                 updateNetworkState(networkState, info);
402             }
403        }
404    };
405
406    private void checkSmsSuplInit(Intent intent) {
407        SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
408        for (int i=0; i <messages.length; i++) {
409            byte[] supl_init = messages[i].getUserData();
410            native_agps_ni_message(supl_init,supl_init.length);
411        }
412    }
413
414    private void checkWapSuplInit(Intent intent) {
415        byte[] supl_init = (byte[]) intent.getExtra("data");
416        native_agps_ni_message(supl_init,supl_init.length);
417    }
418
419    public static boolean isSupported() {
420        return native_is_supported();
421    }
422
423    public GpsLocationProvider(Context context, ILocationManager ilocationManager) {
424        mContext = context;
425        mNtpTime = NtpTrustedTime.getInstance(context);
426        mILocationManager = ilocationManager;
427        mNIHandler = new GpsNetInitiatedHandler(context);
428
429        mLocation.setExtras(mLocationExtras);
430
431        // Create a wake lock
432        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
433        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
434        mWakeLock.setReferenceCounted(true);
435
436        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
437        mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
438        mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
439
440        IntentFilter intentFilter = new IntentFilter();
441        intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
442        intentFilter.addDataScheme("sms");
443        intentFilter.addDataAuthority("localhost","7275");
444        context.registerReceiver(mBroadcastReciever, intentFilter);
445
446        intentFilter = new IntentFilter();
447        intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
448        try {
449            intentFilter.addDataType("application/vnd.omaloc-supl-init");
450        } catch (IntentFilter.MalformedMimeTypeException e) {
451            Log.w(TAG, "Malformed SUPL init mime type");
452        }
453        context.registerReceiver(mBroadcastReciever, intentFilter);
454
455        mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
456
457        // Battery statistics service to be notified when GPS turns on or off
458        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
459
460        mProperties = new Properties();
461        try {
462            File file = new File(PROPERTIES_FILE);
463            FileInputStream stream = new FileInputStream(file);
464            mProperties.load(stream);
465            stream.close();
466
467            mSuplServerHost = mProperties.getProperty("SUPL_HOST");
468            String portString = mProperties.getProperty("SUPL_PORT");
469            if (mSuplServerHost != null && portString != null) {
470                try {
471                    mSuplServerPort = Integer.parseInt(portString);
472                } catch (NumberFormatException e) {
473                    Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
474                }
475            }
476
477            mC2KServerHost = mProperties.getProperty("C2K_HOST");
478            portString = mProperties.getProperty("C2K_PORT");
479            if (mC2KServerHost != null && portString != null) {
480                try {
481                    mC2KServerPort = Integer.parseInt(portString);
482                } catch (NumberFormatException e) {
483                    Log.e(TAG, "unable to parse C2K_PORT: " + portString);
484                }
485            }
486        } catch (IOException e) {
487            Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
488        }
489
490        // wait until we are fully initialized before returning
491        mThread = new GpsLocationProviderThread();
492        mThread.start();
493        while (true) {
494            try {
495                mInitializedLatch.await();
496                break;
497            } catch (InterruptedException e) {
498                Thread.currentThread().interrupt();
499            }
500        }
501    }
502
503    private void initialize() {
504        // register our receiver on our thread rather than the main thread
505        IntentFilter intentFilter = new IntentFilter();
506        intentFilter.addAction(ALARM_WAKEUP);
507        intentFilter.addAction(ALARM_TIMEOUT);
508        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
509        mContext.registerReceiver(mBroadcastReciever, intentFilter);
510    }
511
512    /**
513     * Returns the name of this provider.
514     */
515    @Override
516    public String getName() {
517        return LocationManager.GPS_PROVIDER;
518    }
519
520    @Override
521    public ProviderProperties getProperties() {
522        return PROPERTIES;
523    }
524
525    public void updateNetworkState(int state, NetworkInfo info) {
526        sendMessage(UPDATE_NETWORK_STATE, state, info);
527    }
528
529    private void handleUpdateNetworkState(int state, NetworkInfo info) {
530        mNetworkAvailable = (state == LocationProvider.AVAILABLE);
531
532        if (DEBUG) {
533            Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
534                + " info: " + info);
535        }
536
537        if (info != null) {
538            boolean dataEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
539                                                         Settings.Secure.MOBILE_DATA, 1) == 1;
540            boolean networkAvailable = info.isAvailable() && dataEnabled;
541            String defaultApn = getSelectedApn();
542            if (defaultApn == null) {
543                defaultApn = "dummy-apn";
544            }
545
546            native_update_network_state(info.isConnected(), info.getType(),
547                                        info.isRoaming(), networkAvailable,
548                                        info.getExtraInfo(), defaultApn);
549        }
550
551        if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
552                && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
553            String apnName = info.getExtraInfo();
554            if (mNetworkAvailable) {
555                if (apnName == null) {
556                    /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
557                    exception in the following call to native_agps_data_conn_open*/
558                    apnName = "dummy-apn";
559                }
560                mAGpsApn = apnName;
561                if (DEBUG) Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr);
562                if (mAGpsDataConnectionIpAddr != 0xffffffff) {
563                    boolean route_result;
564                    if (DEBUG) Log.d(TAG, "call requestRouteToHost");
565                    route_result = mConnMgr.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_SUPL,
566                        mAGpsDataConnectionIpAddr);
567                    if (route_result == false) Log.d(TAG, "call requestRouteToHost failed");
568                }
569                if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
570                native_agps_data_conn_open(apnName);
571                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
572            } else {
573                if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
574                mAGpsApn = null;
575                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
576                native_agps_data_conn_failed();
577            }
578        }
579
580        if (mNetworkAvailable) {
581            if (mInjectNtpTimePending == STATE_PENDING_NETWORK) {
582                sendMessage(INJECT_NTP_TIME, 0, null);
583            }
584            if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
585                sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
586            }
587        }
588    }
589
590    private void handleInjectNtpTime() {
591        if (mInjectNtpTimePending == STATE_DOWNLOADING) {
592            // already downloading data
593            return;
594        }
595        if (!mNetworkAvailable) {
596            // try again when network is up
597            mInjectNtpTimePending = STATE_PENDING_NETWORK;
598            return;
599        }
600        mInjectNtpTimePending = STATE_DOWNLOADING;
601
602        // hold wake lock while task runs
603        mWakeLock.acquire();
604        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
605            @Override
606            public void run() {
607                long delay;
608
609                // force refresh NTP cache when outdated
610                if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
611                    mNtpTime.forceRefresh();
612                }
613
614                // only update when NTP time is fresh
615                if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
616                    long time = mNtpTime.getCachedNtpTime();
617                    long timeReference = mNtpTime.getCachedNtpTimeReference();
618                    long certainty = mNtpTime.getCacheCertainty();
619                    long now = System.currentTimeMillis();
620
621                    Log.d(TAG, "NTP server returned: "
622                            + time + " (" + new Date(time)
623                            + ") reference: " + timeReference
624                            + " certainty: " + certainty
625                            + " system time offset: " + (time - now));
626
627                    native_inject_time(time, timeReference, (int) certainty);
628                    delay = NTP_INTERVAL;
629                } else {
630                    if (DEBUG) Log.d(TAG, "requestTime failed");
631                    delay = RETRY_INTERVAL;
632                }
633
634                sendMessage(INJECT_NTP_TIME_FINISHED, 0, null);
635
636                if (mPeriodicTimeInjection) {
637                    // send delayed message for next NTP injection
638                    // since this is delayed and not urgent we do not hold a wake lock here
639                    mHandler.sendEmptyMessageDelayed(INJECT_NTP_TIME, delay);
640                }
641
642                // release wake lock held by task
643                mWakeLock.release();
644            }
645        });
646    }
647
648    private void handleDownloadXtraData() {
649        if (mDownloadXtraDataPending == STATE_DOWNLOADING) {
650            // already downloading data
651            return;
652        }
653        if (!mNetworkAvailable) {
654            // try again when network is up
655            mDownloadXtraDataPending = STATE_PENDING_NETWORK;
656            return;
657        }
658        mDownloadXtraDataPending = STATE_DOWNLOADING;
659
660        // hold wake lock while task runs
661        mWakeLock.acquire();
662        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
663            @Override
664            public void run() {
665                GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
666                byte[] data = xtraDownloader.downloadXtraData();
667                if (data != null) {
668                    if (DEBUG) {
669                        Log.d(TAG, "calling native_inject_xtra_data");
670                    }
671                    native_inject_xtra_data(data, data.length);
672                }
673
674                sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
675
676                if (data == null) {
677                    // try again later
678                    // since this is delayed and not urgent we do not hold a wake lock here
679                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA, RETRY_INTERVAL);
680                }
681
682                // release wake lock held by task
683                mWakeLock.release();
684            }
685        });
686    }
687
688    private void handleUpdateLocation(Location location) {
689        if (location.hasAccuracy()) {
690            native_inject_location(location.getLatitude(), location.getLongitude(),
691                    location.getAccuracy());
692        }
693    }
694
695    /**
696     * Enables this provider.  When enabled, calls to getStatus()
697     * must be handled.  Hardware may be started up
698     * when the provider is enabled.
699     */
700    @Override
701    public void enable() {
702        sendMessage(ENABLE, 1, null);
703    }
704
705    private void handleEnable() {
706        if (DEBUG) Log.d(TAG, "handleEnable");
707
708        synchronized (mLock) {
709            if (mEnabled) return;
710            mEnabled = true;
711        }
712
713        boolean enabled = native_init();
714
715        if (enabled) {
716            mSupportsXtra = native_supports_xtra();
717            if (mSuplServerHost != null) {
718                native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
719            }
720            if (mC2KServerHost != null) {
721                native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
722            }
723        } else {
724            synchronized (mLock) {
725                mEnabled = false;
726            }
727            Log.w(TAG, "Failed to enable location provider");
728        }
729    }
730
731    /**
732     * Disables this provider.  When disabled, calls to getStatus()
733     * need not be handled.  Hardware may be shut
734     * down while the provider is disabled.
735     */
736    @Override
737    public void disable() {
738        sendMessage(ENABLE, 0, null);
739    }
740
741    private void handleDisable() {
742        if (DEBUG) Log.d(TAG, "handleDisable");
743
744        synchronized (mLock) {
745            if (!mEnabled) return;
746            mEnabled = false;
747        }
748
749        stopNavigating();
750        mAlarmManager.cancel(mWakeupIntent);
751        mAlarmManager.cancel(mTimeoutIntent);
752
753        // do this before releasing wakelock
754        native_cleanup();
755    }
756
757    @Override
758    public boolean isEnabled() {
759        synchronized (mLock) {
760            return mEnabled;
761        }
762    }
763
764    @Override
765    public int getStatus(Bundle extras) {
766        if (extras != null) {
767            extras.putInt("satellites", mSvCount);
768        }
769        return mStatus;
770    }
771
772    private void updateStatus(int status, int svCount) {
773        if (status != mStatus || svCount != mSvCount) {
774            mStatus = status;
775            mSvCount = svCount;
776            mLocationExtras.putInt("satellites", svCount);
777            mStatusUpdateTime = SystemClock.elapsedRealtime();
778        }
779    }
780
781    @Override
782    public long getStatusUpdateTime() {
783        return mStatusUpdateTime;
784    }
785
786    @Override
787    public void setRequest(ProviderRequest request, WorkSource source) {
788        sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
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.Secure.getInt(mContext.getContentResolver(),
956                    Settings.Secure.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.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
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 GpsLocationProviderThread extends Thread {
1540
1541        public GpsLocationProviderThread() {
1542            super("GpsLocationProvider");
1543        }
1544
1545        @Override
1546        public void run() {
1547            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1548            initialize();
1549            Looper.prepare();
1550
1551            LocationManager locManager =
1552                    (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
1553            mHandler = new ProviderHandler();
1554            // signal when we are initialized and ready to go
1555            mInitializedLatch.countDown();
1556            locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
1557                    0, 0, new NetworkLocationListener(), Looper.myLooper());
1558            Looper.loop();
1559        }
1560    }
1561
1562    private final class NetworkLocationListener implements LocationListener {
1563        @Override
1564        public void onLocationChanged(Location location) {
1565            // this callback happens on mHandler looper
1566            if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
1567                handleUpdateLocation(location);
1568            }
1569        }
1570        @Override
1571        public void onStatusChanged(String provider, int status, Bundle extras) { }
1572        @Override
1573        public void onProviderEnabled(String provider) { }
1574        @Override
1575        public void onProviderDisabled(String provider) { }
1576    }
1577
1578    private String getSelectedApn() {
1579        Uri uri = Uri.parse("content://telephony/carriers/preferapn");
1580        String apn = null;
1581
1582        Cursor cursor = mContext.getContentResolver().query(uri, new String[] {"apn"},
1583                null, null, Carriers.DEFAULT_SORT_ORDER);
1584
1585        if (null != cursor) {
1586            try {
1587                if (cursor.moveToFirst()) {
1588                    apn = cursor.getString(0);
1589                }
1590            } finally {
1591                cursor.close();
1592            }
1593        }
1594        return apn;
1595    }
1596
1597    @Override
1598    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1599        StringBuilder s = new StringBuilder();
1600        s.append("  mFixInterval=").append(mFixInterval).append("\n");
1601        s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
1602        if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
1603        if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
1604        if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
1605        if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
1606        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
1607        s.append(")\n");
1608
1609        s.append(native_get_internal_state());
1610        pw.append(s);
1611    }
1612
1613    // for GPS SV statistics
1614    private static final int MAX_SVS = 32;
1615    private static final int EPHEMERIS_MASK = 0;
1616    private static final int ALMANAC_MASK = 1;
1617    private static final int USED_FOR_FIX_MASK = 2;
1618
1619    // preallocated arrays, to avoid memory allocation in reportStatus()
1620    private int mSvs[] = new int[MAX_SVS];
1621    private float mSnrs[] = new float[MAX_SVS];
1622    private float mSvElevations[] = new float[MAX_SVS];
1623    private float mSvAzimuths[] = new float[MAX_SVS];
1624    private int mSvMasks[] = new int[3];
1625    private int mSvCount;
1626    // preallocated to avoid memory allocation in reportNmea()
1627    private byte[] mNmeaBuffer = new byte[120];
1628
1629    static { class_init_native(); }
1630    private static native void class_init_native();
1631    private static native boolean native_is_supported();
1632
1633    private native boolean native_init();
1634    private native void native_cleanup();
1635    private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
1636            int preferred_accuracy, int preferred_time);
1637    private native boolean native_start();
1638    private native boolean native_stop();
1639    private native void native_delete_aiding_data(int flags);
1640    // returns number of SVs
1641    // mask[0] is ephemeris mask and mask[1] is almanac mask
1642    private native int native_read_sv_status(int[] svs, float[] snrs,
1643            float[] elevations, float[] azimuths, int[] masks);
1644    private native int native_read_nmea(byte[] buffer, int bufferSize);
1645    private native void native_inject_location(double latitude, double longitude, float accuracy);
1646
1647    // XTRA Support
1648    private native void native_inject_time(long time, long timeReference, int uncertainty);
1649    private native boolean native_supports_xtra();
1650    private native void native_inject_xtra_data(byte[] data, int length);
1651
1652    // DEBUG Support
1653    private native String native_get_internal_state();
1654
1655    // AGPS Support
1656    private native void native_agps_data_conn_open(String apn);
1657    private native void native_agps_data_conn_closed();
1658    private native void native_agps_data_conn_failed();
1659    private native void native_agps_ni_message(byte [] msg, int length);
1660    private native void native_set_agps_server(int type, String hostname, int port);
1661
1662    // Network-initiated (NI) Support
1663    private native void native_send_ni_response(int notificationId, int userResponse);
1664
1665    // AGPS ril suport
1666    private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
1667            int lac, int cid);
1668    private native void native_agps_set_id(int type, String setid);
1669
1670    private native void native_update_network_state(boolean connected, int type,
1671            boolean roaming, boolean available, String extraInfo, String defaultAPN);
1672}
1673