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