GpsLocationProvider.java revision 3bc26143dc81267beba26e50965b4835cf73fdf8
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        updateClientUids(new WorkSource());
795        stopNavigating();
796        mAlarmManager.cancel(mWakeupIntent);
797        mAlarmManager.cancel(mTimeoutIntent);
798
799        // do this before releasing wakelock
800        native_cleanup();
801    }
802
803    @Override
804    public boolean isEnabled() {
805        synchronized (mLock) {
806            return mEnabled;
807        }
808    }
809
810    @Override
811    public int getStatus(Bundle extras) {
812        if (extras != null) {
813            extras.putInt("satellites", mSvCount);
814        }
815        return mStatus;
816    }
817
818    private void updateStatus(int status, int svCount) {
819        if (status != mStatus || svCount != mSvCount) {
820            mStatus = status;
821            mSvCount = svCount;
822            mLocationExtras.putInt("satellites", svCount);
823            mStatusUpdateTime = SystemClock.elapsedRealtime();
824        }
825    }
826
827    @Override
828    public long getStatusUpdateTime() {
829        return mStatusUpdateTime;
830    }
831
832    @Override
833    public void setRequest(ProviderRequest request, WorkSource source) {
834        sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
835    }
836
837    private void handleSetRequest(ProviderRequest request, WorkSource source) {
838        boolean singleShot = false;
839
840        // see if the request is for a single update
841        if (request.locationRequests != null && request.locationRequests.size() > 0) {
842            // if any request has zero or more than one updates
843            // requested, then this is not single-shot mode
844            singleShot = true;
845
846            for (LocationRequest lr : request.locationRequests) {
847                if (lr.getNumUpdates() != 1) {
848                    singleShot = false;
849                }
850            }
851        }
852
853        if (DEBUG) Log.d(TAG, "setRequest " + request);
854        if (request.reportLocation) {
855            // update client uids
856            updateClientUids(source);
857
858            mFixInterval = (int) request.interval;
859
860            // check for overflow
861            if (mFixInterval != request.interval) {
862                Log.w(TAG, "interval overflow: " + request.interval);
863                mFixInterval = Integer.MAX_VALUE;
864            }
865
866            // apply request to GPS engine
867            if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
868                // change period
869                if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
870                        mFixInterval, 0, 0)) {
871                    Log.e(TAG, "set_position_mode failed in setMinTime()");
872                }
873            } else if (!mStarted) {
874                // start GPS
875                startNavigating(singleShot);
876            }
877        } else {
878            updateClientUids(new WorkSource());
879
880            stopNavigating();
881            mAlarmManager.cancel(mWakeupIntent);
882            mAlarmManager.cancel(mTimeoutIntent);
883        }
884    }
885
886    private final class Listener implements IBinder.DeathRecipient {
887        final IGpsStatusListener mListener;
888
889        Listener(IGpsStatusListener listener) {
890            mListener = listener;
891        }
892
893        @Override
894        public void binderDied() {
895            if (DEBUG) Log.d(TAG, "GPS status listener died");
896
897            synchronized (mListeners) {
898                mListeners.remove(this);
899            }
900            if (mListener != null) {
901                mListener.asBinder().unlinkToDeath(this, 0);
902            }
903        }
904    }
905
906    private void updateClientUids(WorkSource source) {
907        // Update work source.
908        WorkSource[] changes = mClientSource.setReturningDiffs(source);
909        if (changes == null) {
910            return;
911        }
912        WorkSource newWork = changes[0];
913        WorkSource goneWork = changes[1];
914
915        // Update sources that were not previously tracked.
916        if (newWork != null) {
917            int lastuid = -1;
918            for (int i=0; i<newWork.size(); i++) {
919                try {
920                    int uid = newWork.get(i);
921                    mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
922                            AppOpsManager.OP_GPS, uid, newWork.getName(i));
923                    if (uid != lastuid) {
924                        lastuid = uid;
925                        mBatteryStats.noteStartGps(uid);
926                    }
927                } catch (RemoteException e) {
928                    Log.w(TAG, "RemoteException", e);
929                }
930            }
931        }
932
933        // Update sources that are no longer tracked.
934        if (goneWork != null) {
935            int lastuid = -1;
936            for (int i=0; i<goneWork.size(); i++) {
937                try {
938                    int uid = goneWork.get(i);
939                    mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
940                            AppOpsManager.OP_GPS, uid, goneWork.getName(i));
941                    if (uid != lastuid) {
942                        lastuid = uid;
943                        mBatteryStats.noteStopGps(uid);
944                    }
945                } catch (RemoteException e) {
946                    Log.w(TAG, "RemoteException", e);
947                }
948            }
949        }
950    }
951
952    @Override
953    public boolean sendExtraCommand(String command, Bundle extras) {
954
955        long identity = Binder.clearCallingIdentity();
956        boolean result = false;
957
958        if ("delete_aiding_data".equals(command)) {
959            result = deleteAidingData(extras);
960        } else if ("force_time_injection".equals(command)) {
961            sendMessage(INJECT_NTP_TIME, 0, null);
962            result = true;
963        } else if ("force_xtra_injection".equals(command)) {
964            if (mSupportsXtra) {
965                xtraDownloadRequest();
966                result = true;
967            }
968        } else {
969            Log.w(TAG, "sendExtraCommand: unknown command " + command);
970        }
971
972        Binder.restoreCallingIdentity(identity);
973        return result;
974    }
975
976    private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
977        public boolean isHardwareGeofenceSupported() {
978            return native_is_geofence_supported();
979        }
980
981        public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
982                double longitude, double radius, int lastTransition, int monitorTransitions,
983                int notificationResponsiveness, int unknownTimer) {
984            return native_add_geofence(geofenceId, latitude, longitude, radius,
985                    lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer);
986        }
987
988        public boolean removeHardwareGeofence(int geofenceId) {
989            return native_remove_geofence(geofenceId);
990        }
991
992        public boolean pauseHardwareGeofence(int geofenceId) {
993            return native_pause_geofence(geofenceId);
994        }
995
996        public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) {
997            return native_resume_geofence(geofenceId, monitorTransition);
998        }
999    };
1000
1001    private boolean deleteAidingData(Bundle extras) {
1002        int flags;
1003
1004        if (extras == null) {
1005            flags = GPS_DELETE_ALL;
1006        } else {
1007            flags = 0;
1008            if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
1009            if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
1010            if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
1011            if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
1012            if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
1013            if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
1014            if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
1015            if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
1016            if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
1017            if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
1018            if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
1019            if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
1020            if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
1021        }
1022
1023        if (flags != 0) {
1024            native_delete_aiding_data(flags);
1025            return true;
1026        }
1027
1028        return false;
1029    }
1030
1031    private void startNavigating(boolean singleShot) {
1032        if (!mStarted) {
1033            if (DEBUG) Log.d(TAG, "startNavigating, singleShot is " + singleShot);
1034            mTimeToFirstFix = 0;
1035            mLastFixTime = 0;
1036            mStarted = true;
1037            mSingleShot = singleShot;
1038            mPositionMode = GPS_POSITION_MODE_STANDALONE;
1039
1040             if (Settings.Global.getInt(mContext.getContentResolver(),
1041                    Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0) {
1042                if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
1043                    mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
1044                } else if (hasCapability(GPS_CAPABILITY_MSB)) {
1045                    mPositionMode = GPS_POSITION_MODE_MS_BASED;
1046                }
1047            }
1048
1049            if (DEBUG) {
1050                String mode;
1051
1052                switch(mPositionMode) {
1053                    case GPS_POSITION_MODE_STANDALONE:
1054                        mode = "standalone";
1055                        break;
1056                    case GPS_POSITION_MODE_MS_ASSISTED:
1057                        mode = "MS_ASSISTED";
1058                        break;
1059                    case GPS_POSITION_MODE_MS_BASED:
1060                        mode = "MS_BASED";
1061                        break;
1062                    default:
1063                        mode = "unknown";
1064                        break;
1065                }
1066                Log.d(TAG, "setting position_mode to " + mode);
1067            }
1068
1069            int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
1070            if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
1071                    interval, 0, 0)) {
1072                mStarted = false;
1073                Log.e(TAG, "set_position_mode failed in startNavigating()");
1074                return;
1075            }
1076            if (!native_start()) {
1077                mStarted = false;
1078                Log.e(TAG, "native_start failed in startNavigating()");
1079                return;
1080            }
1081
1082            // reset SV count to zero
1083            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
1084            mFixRequestTime = System.currentTimeMillis();
1085            if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
1086                // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
1087                // and our fix interval is not short
1088                if (mFixInterval >= NO_FIX_TIMEOUT) {
1089                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1090                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
1091                }
1092            }
1093        }
1094    }
1095
1096    private void stopNavigating() {
1097        if (DEBUG) Log.d(TAG, "stopNavigating");
1098        if (mStarted) {
1099            mStarted = false;
1100            mSingleShot = false;
1101            native_stop();
1102            mTimeToFirstFix = 0;
1103            mLastFixTime = 0;
1104            mLocationFlags = LOCATION_INVALID;
1105
1106            // reset SV count to zero
1107            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
1108        }
1109    }
1110
1111    private void hibernate() {
1112        // stop GPS until our next fix interval arrives
1113        stopNavigating();
1114        mAlarmManager.cancel(mTimeoutIntent);
1115        mAlarmManager.cancel(mWakeupIntent);
1116        long now = SystemClock.elapsedRealtime();
1117        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent);
1118    }
1119
1120    private boolean hasCapability(int capability) {
1121        return ((mEngineCapabilities & capability) != 0);
1122    }
1123
1124
1125    /**
1126     * called from native code to update our position.
1127     */
1128    private void reportLocation(int flags, double latitude, double longitude, double altitude,
1129            float speed, float bearing, float accuracy, long timestamp) {
1130        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
1131                " timestamp: " + timestamp);
1132
1133        synchronized (mLocation) {
1134            mLocationFlags = flags;
1135            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1136                mLocation.setLatitude(latitude);
1137                mLocation.setLongitude(longitude);
1138                mLocation.setTime(timestamp);
1139                // It would be nice to push the elapsed real-time timestamp
1140                // further down the stack, but this is still useful
1141                mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1142            }
1143            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1144                mLocation.setAltitude(altitude);
1145            } else {
1146                mLocation.removeAltitude();
1147            }
1148            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1149                mLocation.setSpeed(speed);
1150            } else {
1151                mLocation.removeSpeed();
1152            }
1153            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1154                mLocation.setBearing(bearing);
1155            } else {
1156                mLocation.removeBearing();
1157            }
1158            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1159                mLocation.setAccuracy(accuracy);
1160            } else {
1161                mLocation.removeAccuracy();
1162            }
1163            mLocation.setExtras(mLocationExtras);
1164
1165            try {
1166                mILocationManager.reportLocation(mLocation, false);
1167            } catch (RemoteException e) {
1168                Log.e(TAG, "RemoteException calling reportLocation");
1169            }
1170        }
1171
1172        mLastFixTime = System.currentTimeMillis();
1173        // report time to first fix
1174        if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1175            mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
1176            if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
1177
1178            // notify status listeners
1179            synchronized (mListeners) {
1180                int size = mListeners.size();
1181                for (int i = 0; i < size; i++) {
1182                    Listener listener = mListeners.get(i);
1183                    try {
1184                        listener.mListener.onFirstFix(mTimeToFirstFix);
1185                    } catch (RemoteException e) {
1186                        Log.w(TAG, "RemoteException in stopNavigating");
1187                        mListeners.remove(listener);
1188                        // adjust for size of list changing
1189                        size--;
1190                    }
1191                }
1192            }
1193        }
1194
1195        if (mSingleShot) {
1196            stopNavigating();
1197        }
1198
1199        if (mStarted && mStatus != LocationProvider.AVAILABLE) {
1200            // we want to time out if we do not receive a fix
1201            // within the time out and we are requesting infrequent fixes
1202            if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
1203                mAlarmManager.cancel(mTimeoutIntent);
1204            }
1205
1206            // send an intent to notify that the GPS is receiving fixes.
1207            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1208            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
1209            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1210            updateStatus(LocationProvider.AVAILABLE, mSvCount);
1211        }
1212
1213       if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
1214               mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
1215            if (DEBUG) Log.d(TAG, "got fix, hibernating");
1216            hibernate();
1217        }
1218   }
1219
1220    /**
1221     * called from native code to update our status
1222     */
1223    private void reportStatus(int status) {
1224        if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
1225
1226        synchronized (mListeners) {
1227            boolean wasNavigating = mNavigating;
1228
1229            switch (status) {
1230                case GPS_STATUS_SESSION_BEGIN:
1231                    mNavigating = true;
1232                    mEngineOn = true;
1233                    break;
1234                case GPS_STATUS_SESSION_END:
1235                    mNavigating = false;
1236                    break;
1237                case GPS_STATUS_ENGINE_ON:
1238                    mEngineOn = true;
1239                    break;
1240                case GPS_STATUS_ENGINE_OFF:
1241                    mEngineOn = false;
1242                    mNavigating = false;
1243                    break;
1244            }
1245
1246            if (wasNavigating != mNavigating) {
1247                int size = mListeners.size();
1248                for (int i = 0; i < size; i++) {
1249                    Listener listener = mListeners.get(i);
1250                    try {
1251                        if (mNavigating) {
1252                            listener.mListener.onGpsStarted();
1253                        } else {
1254                            listener.mListener.onGpsStopped();
1255                        }
1256                    } catch (RemoteException e) {
1257                        Log.w(TAG, "RemoteException in reportStatus");
1258                        mListeners.remove(listener);
1259                        // adjust for size of list changing
1260                        size--;
1261                    }
1262                }
1263
1264                // send an intent to notify that the GPS has been enabled or disabled.
1265                Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
1266                intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
1267                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1268            }
1269        }
1270    }
1271
1272    /**
1273     * called from native code to update SV info
1274     */
1275    private void reportSvStatus() {
1276
1277        int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
1278
1279        synchronized (mListeners) {
1280            int size = mListeners.size();
1281            for (int i = 0; i < size; i++) {
1282                Listener listener = mListeners.get(i);
1283                try {
1284                    listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
1285                            mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
1286                            mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
1287                } catch (RemoteException e) {
1288                    Log.w(TAG, "RemoteException in reportSvInfo");
1289                    mListeners.remove(listener);
1290                    // adjust for size of list changing
1291                    size--;
1292                }
1293            }
1294        }
1295
1296        if (VERBOSE) {
1297            Log.v(TAG, "SV count: " + svCount +
1298                    " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
1299                    " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
1300            for (int i = 0; i < svCount; i++) {
1301                Log.v(TAG, "sv: " + mSvs[i] +
1302                        " snr: " + mSnrs[i]/10 +
1303                        " elev: " + mSvElevations[i] +
1304                        " azimuth: " + mSvAzimuths[i] +
1305                        ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " E") +
1306                        ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " A") +
1307                        ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
1308            }
1309        }
1310
1311        // return number of sets used in fix instead of total
1312        updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
1313
1314        if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
1315            System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
1316            // send an intent to notify that the GPS is no longer receiving fixes.
1317            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1318            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
1319            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1320            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
1321        }
1322    }
1323
1324    /**
1325     * called from native code to update AGPS status
1326     */
1327    private void reportAGpsStatus(int type, int status, int ipaddr) {
1328        switch (status) {
1329            case GPS_REQUEST_AGPS_DATA_CONN:
1330                if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
1331                // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
1332                //  to avoid a race condition with handleUpdateNetworkState()
1333                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
1334                int result = mConnMgr.startUsingNetworkFeature(
1335                        ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1336                mAGpsDataConnectionIpAddr = ipaddr;
1337                if (result == PhoneConstants.APN_ALREADY_ACTIVE) {
1338                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE");
1339                    if (mAGpsApn != null) {
1340                        Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr);
1341                        if (mAGpsDataConnectionIpAddr != 0xffffffff) {
1342                            boolean route_result;
1343                            if (DEBUG) Log.d(TAG, "call requestRouteToHost");
1344                            route_result = mConnMgr.requestRouteToHost(
1345                                ConnectivityManager.TYPE_MOBILE_SUPL,
1346                                mAGpsDataConnectionIpAddr);
1347                            if (route_result == false) Log.d(TAG, "call requestRouteToHost failed");
1348                        }
1349                        native_agps_data_conn_open(mAGpsApn);
1350                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
1351                    } else {
1352                        Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE");
1353                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1354                        native_agps_data_conn_failed();
1355                    }
1356                } else if (result == PhoneConstants.APN_REQUEST_STARTED) {
1357                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_REQUEST_STARTED");
1358                    // Nothing to do here
1359                } else {
1360                    if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed, value is " +
1361                                     result);
1362                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1363                    native_agps_data_conn_failed();
1364                }
1365                break;
1366            case GPS_RELEASE_AGPS_DATA_CONN:
1367                if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
1368                if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
1369                    mConnMgr.stopUsingNetworkFeature(
1370                            ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1371                    native_agps_data_conn_closed();
1372                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1373                }
1374                break;
1375            case GPS_AGPS_DATA_CONNECTED:
1376                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
1377                break;
1378            case GPS_AGPS_DATA_CONN_DONE:
1379                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
1380                break;
1381            case GPS_AGPS_DATA_CONN_FAILED:
1382                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
1383                break;
1384        }
1385    }
1386
1387    /**
1388     * called from native code to report NMEA data received
1389     */
1390    private void reportNmea(long timestamp) {
1391        synchronized (mListeners) {
1392            int size = mListeners.size();
1393            if (size > 0) {
1394                // don't bother creating the String if we have no listeners
1395                int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
1396                String nmea = new String(mNmeaBuffer, 0, length);
1397
1398                for (int i = 0; i < size; i++) {
1399                    Listener listener = mListeners.get(i);
1400                    try {
1401                        listener.mListener.onNmeaReceived(timestamp, nmea);
1402                    } catch (RemoteException e) {
1403                        Log.w(TAG, "RemoteException in reportNmea");
1404                        mListeners.remove(listener);
1405                        // adjust for size of list changing
1406                        size--;
1407                    }
1408                }
1409            }
1410        }
1411    }
1412
1413    /**
1414     * called from native code to inform us what the GPS engine capabilities are
1415     */
1416    private void setEngineCapabilities(int capabilities) {
1417        mEngineCapabilities = capabilities;
1418
1419        if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
1420            mPeriodicTimeInjection = true;
1421            requestUtcTime();
1422        }
1423    }
1424
1425    /**
1426     * called from native code to request XTRA data
1427     */
1428    private void xtraDownloadRequest() {
1429        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
1430        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
1431    }
1432
1433    /**
1434     * Helper method to construct a location object.
1435     */
1436    private Location buildLocation(
1437            int flags,
1438            double latitude,
1439            double longitude,
1440            double altitude,
1441            float speed,
1442            float bearing,
1443            float accuracy,
1444            long timestamp) {
1445        Location location = new Location(LocationManager.GPS_PROVIDER);
1446        if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1447            location.setLatitude(latitude);
1448            location.setLongitude(longitude);
1449            location.setTime(timestamp);
1450            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1451        }
1452        if((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1453            location.setAltitude(altitude);
1454        }
1455        if((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1456            location.setSpeed(speed);
1457        }
1458        if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1459            location.setBearing(bearing);
1460        }
1461        if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1462            location.setAccuracy(accuracy);
1463        }
1464        return location;
1465    }
1466
1467    /**
1468     * Converts the GPS HAL status to the internal Geofence Hardware status.
1469     */
1470    private int getGeofenceStatus(int status) {
1471        switch(status) {
1472            case GPS_GEOFENCE_OPERATION_SUCCESS:
1473                return GeofenceHardware.GEOFENCE_SUCCESS;
1474            case GPS_GEOFENCE_ERROR_GENERIC:
1475                return GeofenceHardware.GEOFENCE_FAILURE;
1476            case GPS_GEOFENCE_ERROR_ID_EXISTS:
1477                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
1478            case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
1479                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
1480            case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
1481                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
1482            case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
1483                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
1484            default:
1485                return -1;
1486        }
1487    }
1488
1489    /**
1490     * Called from native to report GPS Geofence transition
1491     * All geofence callbacks are called on the same thread
1492     */
1493    private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
1494            double longitude, double altitude, float speed, float bearing, float accuracy,
1495            long timestamp, int transition, long transitionTimestamp) {
1496        if (mGeofenceHardwareImpl == null) {
1497            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1498        }
1499        Location location = buildLocation(
1500                flags,
1501                latitude,
1502                longitude,
1503                altitude,
1504                speed,
1505                bearing,
1506                accuracy,
1507                timestamp);
1508        mGeofenceHardwareImpl.reportGeofenceTransition(
1509                geofenceId,
1510                location,
1511                transition,
1512                transitionTimestamp,
1513                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1514                FusedBatchOptions.SourceTechnologies.GNSS);
1515    }
1516
1517    /**
1518     * called from native code to report GPS status change.
1519     */
1520    private void reportGeofenceStatus(int status, int flags, double latitude,
1521            double longitude, double altitude, float speed, float bearing, float accuracy,
1522            long timestamp) {
1523        if (mGeofenceHardwareImpl == null) {
1524            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1525        }
1526        Location location = buildLocation(
1527                flags,
1528                latitude,
1529                longitude,
1530                altitude,
1531                speed,
1532                bearing,
1533                accuracy,
1534                timestamp);
1535        int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
1536        if(status == GPS_GEOFENCE_AVAILABLE) {
1537            monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
1538        }
1539        mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
1540                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1541                monitorStatus,
1542                location,
1543                FusedBatchOptions.SourceTechnologies.GNSS);
1544    }
1545
1546    /**
1547     * called from native code - Geofence Add callback
1548     */
1549    private void reportGeofenceAddStatus(int geofenceId, int status) {
1550        if (mGeofenceHardwareImpl == null) {
1551            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1552        }
1553        mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
1554    }
1555
1556    /**
1557     * called from native code - Geofence Remove callback
1558     */
1559    private void reportGeofenceRemoveStatus(int geofenceId, int status) {
1560        if (mGeofenceHardwareImpl == null) {
1561            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1562        }
1563        mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
1564    }
1565
1566    /**
1567     * called from native code - Geofence Pause callback
1568     */
1569    private void reportGeofencePauseStatus(int geofenceId, int status) {
1570        if (mGeofenceHardwareImpl == null) {
1571            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1572        }
1573        mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
1574    }
1575
1576    /**
1577     * called from native code - Geofence Resume callback
1578     */
1579    private void reportGeofenceResumeStatus(int geofenceId, int status) {
1580        if (mGeofenceHardwareImpl == null) {
1581            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1582        }
1583        mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
1584    }
1585
1586    //=============================================================
1587    // NI Client support
1588    //=============================================================
1589    private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
1590        // Sends a response for an NI reqeust to HAL.
1591        @Override
1592        public boolean sendNiResponse(int notificationId, int userResponse)
1593        {
1594            // TODO Add Permission check
1595
1596            if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
1597                    ", response: " + userResponse);
1598            native_send_ni_response(notificationId, userResponse);
1599            return true;
1600        }
1601    };
1602
1603    public INetInitiatedListener getNetInitiatedListener() {
1604        return mNetInitiatedListener;
1605    }
1606
1607    // Called by JNI function to report an NI request.
1608    public void reportNiNotification(
1609            int notificationId,
1610            int niType,
1611            int notifyFlags,
1612            int timeout,
1613            int defaultResponse,
1614            String requestorId,
1615            String text,
1616            int requestorIdEncoding,
1617            int textEncoding,
1618            String extras  // Encoded extra data
1619        )
1620    {
1621        Log.i(TAG, "reportNiNotification: entered");
1622        Log.i(TAG, "notificationId: " + notificationId +
1623                ", niType: " + niType +
1624                ", notifyFlags: " + notifyFlags +
1625                ", timeout: " + timeout +
1626                ", defaultResponse: " + defaultResponse);
1627
1628        Log.i(TAG, "requestorId: " + requestorId +
1629                ", text: " + text +
1630                ", requestorIdEncoding: " + requestorIdEncoding +
1631                ", textEncoding: " + textEncoding);
1632
1633        GpsNiNotification notification = new GpsNiNotification();
1634
1635        notification.notificationId = notificationId;
1636        notification.niType = niType;
1637        notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
1638        notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
1639        notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
1640        notification.timeout = timeout;
1641        notification.defaultResponse = defaultResponse;
1642        notification.requestorId = requestorId;
1643        notification.text = text;
1644        notification.requestorIdEncoding = requestorIdEncoding;
1645        notification.textEncoding = textEncoding;
1646
1647        // Process extras, assuming the format is
1648        // one of more lines of "key = value"
1649        Bundle bundle = new Bundle();
1650
1651        if (extras == null) extras = "";
1652        Properties extraProp = new Properties();
1653
1654        try {
1655            extraProp.load(new StringReader(extras));
1656        }
1657        catch (IOException e)
1658        {
1659            Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
1660        }
1661
1662        for (Entry<Object, Object> ent : extraProp.entrySet())
1663        {
1664            bundle.putString((String) ent.getKey(), (String) ent.getValue());
1665        }
1666
1667        notification.extras = bundle;
1668
1669        mNIHandler.handleNiNotification(notification);
1670    }
1671
1672    /**
1673     * Called from native code to request set id info.
1674     * We should be careful about receiving null string from the TelephonyManager,
1675     * because sending null String to JNI function would cause a crash.
1676     */
1677
1678    private void requestSetID(int flags) {
1679        TelephonyManager phone = (TelephonyManager)
1680                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1681        int    type = AGPS_SETID_TYPE_NONE;
1682        String data = "";
1683
1684        if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
1685            String data_temp = phone.getSubscriberId();
1686            if (data_temp == null) {
1687                // This means the framework does not have the SIM card ready.
1688            } else {
1689                // This means the framework has the SIM card.
1690                data = data_temp;
1691                type = AGPS_SETID_TYPE_IMSI;
1692            }
1693        }
1694        else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
1695            String data_temp = phone.getLine1Number();
1696            if (data_temp == null) {
1697                // This means the framework does not have the SIM card ready.
1698            } else {
1699                // This means the framework has the SIM card.
1700                data = data_temp;
1701                type = AGPS_SETID_TYPE_MSISDN;
1702            }
1703        }
1704        native_agps_set_id(type, data);
1705    }
1706
1707    /**
1708     * Called from native code to request utc time info
1709     */
1710
1711    private void requestUtcTime() {
1712        sendMessage(INJECT_NTP_TIME, 0, null);
1713    }
1714
1715    /**
1716     * Called from native code to request reference location info
1717     */
1718
1719    private void requestRefLocation(int flags) {
1720        TelephonyManager phone = (TelephonyManager)
1721                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1722        final int phoneType = phone.getPhoneType();
1723        if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
1724            GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
1725            if ((gsm_cell != null) && (phone.getNetworkOperator() != null)
1726                    && (phone.getNetworkOperator().length() > 3)) {
1727                int type;
1728                int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
1729                int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
1730                int networkType = phone.getNetworkType();
1731                if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
1732                    || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
1733                    || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
1734                    || networkType == TelephonyManager.NETWORK_TYPE_HSPA
1735                    || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
1736                    type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
1737                } else {
1738                    type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
1739                }
1740                native_agps_set_ref_location_cellid(type, mcc, mnc,
1741                        gsm_cell.getLac(), gsm_cell.getCid());
1742            } else {
1743                Log.e(TAG,"Error getting cell location info.");
1744            }
1745        } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
1746            Log.e(TAG, "CDMA not supported.");
1747        }
1748    }
1749
1750    private void sendMessage(int message, int arg, Object obj) {
1751        // hold a wake lock until this message is delivered
1752        // note that this assumes the message will not be removed from the queue before
1753        // it is handled (otherwise the wake lock would be leaked).
1754        mWakeLock.acquire();
1755        mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
1756    }
1757
1758    private final class ProviderHandler extends Handler {
1759        public ProviderHandler(Looper looper) {
1760            super(looper, null, true /*async*/);
1761        }
1762
1763        @Override
1764        public void handleMessage(Message msg) {
1765            int message = msg.what;
1766            switch (message) {
1767                case ENABLE:
1768                    if (msg.arg1 == 1) {
1769                        handleEnable();
1770                    } else {
1771                        handleDisable();
1772                    }
1773                    break;
1774                case SET_REQUEST:
1775                    GpsRequest gpsRequest = (GpsRequest) msg.obj;
1776                    handleSetRequest(gpsRequest.request, gpsRequest.source);
1777                    break;
1778                case UPDATE_NETWORK_STATE:
1779                    handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
1780                    break;
1781                case INJECT_NTP_TIME:
1782                    handleInjectNtpTime();
1783                    break;
1784                case DOWNLOAD_XTRA_DATA:
1785                    if (mSupportsXtra) {
1786                        handleDownloadXtraData();
1787                    }
1788                    break;
1789                case INJECT_NTP_TIME_FINISHED:
1790                    mInjectNtpTimePending = STATE_IDLE;
1791                    break;
1792                case DOWNLOAD_XTRA_DATA_FINISHED:
1793                    mDownloadXtraDataPending = STATE_IDLE;
1794                    break;
1795                case UPDATE_LOCATION:
1796                    handleUpdateLocation((Location)msg.obj);
1797                    break;
1798            }
1799            if (msg.arg2 == 1) {
1800                // wakelock was taken for this message, release it
1801                mWakeLock.release();
1802            }
1803        }
1804    };
1805
1806    private final class NetworkLocationListener implements LocationListener {
1807        @Override
1808        public void onLocationChanged(Location location) {
1809            // this callback happens on mHandler looper
1810            if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
1811                handleUpdateLocation(location);
1812            }
1813        }
1814        @Override
1815        public void onStatusChanged(String provider, int status, Bundle extras) { }
1816        @Override
1817        public void onProviderEnabled(String provider) { }
1818        @Override
1819        public void onProviderDisabled(String provider) { }
1820    }
1821
1822    private String getSelectedApn() {
1823        Uri uri = Uri.parse("content://telephony/carriers/preferapn");
1824        String apn = null;
1825
1826        Cursor cursor = mContext.getContentResolver().query(uri, new String[] {"apn"},
1827                null, null, Carriers.DEFAULT_SORT_ORDER);
1828
1829        if (null != cursor) {
1830            try {
1831                if (cursor.moveToFirst()) {
1832                    apn = cursor.getString(0);
1833                }
1834            } finally {
1835                cursor.close();
1836            }
1837        }
1838        return apn;
1839    }
1840
1841    @Override
1842    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1843        StringBuilder s = new StringBuilder();
1844        s.append("  mFixInterval=").append(mFixInterval).append("\n");
1845        s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
1846        if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
1847        if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
1848        if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
1849        if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
1850        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
1851        s.append(")\n");
1852
1853        s.append(native_get_internal_state());
1854        pw.append(s);
1855    }
1856
1857    // for GPS SV statistics
1858    private static final int MAX_SVS = 32;
1859    private static final int EPHEMERIS_MASK = 0;
1860    private static final int ALMANAC_MASK = 1;
1861    private static final int USED_FOR_FIX_MASK = 2;
1862
1863    // preallocated arrays, to avoid memory allocation in reportStatus()
1864    private int mSvs[] = new int[MAX_SVS];
1865    private float mSnrs[] = new float[MAX_SVS];
1866    private float mSvElevations[] = new float[MAX_SVS];
1867    private float mSvAzimuths[] = new float[MAX_SVS];
1868    private int mSvMasks[] = new int[3];
1869    private int mSvCount;
1870    // preallocated to avoid memory allocation in reportNmea()
1871    private byte[] mNmeaBuffer = new byte[120];
1872
1873    static { class_init_native(); }
1874    private static native void class_init_native();
1875    private static native boolean native_is_supported();
1876
1877    private native boolean native_init();
1878    private native void native_cleanup();
1879    private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
1880            int preferred_accuracy, int preferred_time);
1881    private native boolean native_start();
1882    private native boolean native_stop();
1883    private native void native_delete_aiding_data(int flags);
1884    // returns number of SVs
1885    // mask[0] is ephemeris mask and mask[1] is almanac mask
1886    private native int native_read_sv_status(int[] svs, float[] snrs,
1887            float[] elevations, float[] azimuths, int[] masks);
1888    private native int native_read_nmea(byte[] buffer, int bufferSize);
1889    private native void native_inject_location(double latitude, double longitude, float accuracy);
1890
1891    // XTRA Support
1892    private native void native_inject_time(long time, long timeReference, int uncertainty);
1893    private native boolean native_supports_xtra();
1894    private native void native_inject_xtra_data(byte[] data, int length);
1895
1896    // DEBUG Support
1897    private native String native_get_internal_state();
1898
1899    // AGPS Support
1900    private native void native_agps_data_conn_open(String apn);
1901    private native void native_agps_data_conn_closed();
1902    private native void native_agps_data_conn_failed();
1903    private native void native_agps_ni_message(byte [] msg, int length);
1904    private native void native_set_agps_server(int type, String hostname, int port);
1905
1906    // Network-initiated (NI) Support
1907    private native void native_send_ni_response(int notificationId, int userResponse);
1908
1909    // AGPS ril suport
1910    private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
1911            int lac, int cid);
1912    private native void native_agps_set_id(int type, String setid);
1913
1914    private native void native_update_network_state(boolean connected, int type,
1915            boolean roaming, boolean available, String extraInfo, String defaultAPN);
1916
1917    // Hardware Geofence support.
1918    private static native boolean native_is_geofence_supported();
1919    private static native boolean native_add_geofence(int geofenceId, double latitude,
1920            double longitude, double radius, int lastTransition,int monitorTransitions,
1921            int notificationResponsivenes, int unknownTimer);
1922    private static native boolean native_remove_geofence(int geofenceId);
1923    private static native boolean native_resume_geofence(int geofenceId, int transitions);
1924    private static native boolean native_pause_geofence(int geofenceId);
1925}
1926