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 com.android.internal.app.IAppOpsService;
20import com.android.internal.app.IBatteryStats;
21import com.android.internal.location.GpsNetInitiatedHandler;
22import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
23import com.android.internal.location.ProviderProperties;
24import com.android.internal.location.ProviderRequest;
25
26import android.app.AlarmManager;
27import android.app.AppOpsManager;
28import android.app.PendingIntent;
29import android.content.BroadcastReceiver;
30import android.content.Context;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.database.Cursor;
34import android.hardware.location.GeofenceHardware;
35import android.hardware.location.GeofenceHardwareImpl;
36import android.location.Criteria;
37import android.location.FusedBatchOptions;
38import android.location.GnssStatus;
39import android.location.IGnssStatusListener;
40import android.location.IGnssStatusProvider;
41import android.location.GnssMeasurementsEvent;
42import android.location.GnssNavigationMessage;
43import android.location.IGpsGeofenceHardware;
44import android.location.ILocationManager;
45import android.location.INetInitiatedListener;
46import android.location.Location;
47import android.location.LocationListener;
48import android.location.LocationManager;
49import android.location.LocationProvider;
50import android.location.LocationRequest;
51import android.net.ConnectivityManager;
52import android.net.Network;
53import android.net.NetworkCapabilities;
54import android.net.NetworkInfo;
55import android.net.NetworkRequest;
56import android.net.Uri;
57import android.os.AsyncTask;
58import android.os.BatteryStats;
59import android.os.Binder;
60import android.os.Bundle;
61import android.os.Handler;
62import android.os.Looper;
63import android.os.Message;
64import android.os.PowerManager;
65import android.os.RemoteException;
66import android.os.ServiceManager;
67import android.os.SystemClock;
68import android.os.SystemProperties;
69import android.os.UserHandle;
70import android.os.WorkSource;
71import android.provider.Settings;
72import android.provider.Telephony.Carriers;
73import android.provider.Telephony.Sms.Intents;
74import android.telephony.SmsMessage;
75import android.telephony.SubscriptionManager;
76import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
77import android.telephony.TelephonyManager;
78import android.telephony.gsm.GsmCellLocation;
79import android.text.TextUtils;
80import android.util.Log;
81import android.util.NtpTrustedTime;
82
83import java.io.ByteArrayOutputStream;
84import java.io.File;
85import java.io.FileDescriptor;
86import java.io.FileInputStream;
87import java.io.IOException;
88import java.io.PrintWriter;
89import java.io.StringReader;
90import java.net.InetAddress;
91import java.net.UnknownHostException;
92import java.util.Arrays;
93import java.util.Date;
94import java.util.Map.Entry;
95import java.util.Properties;
96
97import libcore.io.IoUtils;
98
99/**
100 * A GPS implementation of LocationProvider used by LocationManager.
101 *
102 * {@hide}
103 */
104public class GnssLocationProvider implements LocationProviderInterface {
105
106    private static final String TAG = "GnssLocationProvider";
107
108    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
109    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
110
111    private static final ProviderProperties PROPERTIES = new ProviderProperties(
112            true, true, false, false, true, true, true,
113            Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
114
115    // these need to match GpsPositionMode enum in gps.h
116    private static final int GPS_POSITION_MODE_STANDALONE = 0;
117    private static final int GPS_POSITION_MODE_MS_BASED = 1;
118    private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
119
120    // these need to match GpsPositionRecurrence enum in gps.h
121    private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
122    private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
123
124    // these need to match GpsStatusValue defines in gps.h
125    private static final int GPS_STATUS_NONE = 0;
126    private static final int GPS_STATUS_SESSION_BEGIN = 1;
127    private static final int GPS_STATUS_SESSION_END = 2;
128    private static final int GPS_STATUS_ENGINE_ON = 3;
129    private static final int GPS_STATUS_ENGINE_OFF = 4;
130
131    // these need to match GpsApgsStatusValue defines in gps.h
132    /** AGPS status event values. */
133    private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
134    private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
135    private static final int GPS_AGPS_DATA_CONNECTED = 3;
136    private static final int GPS_AGPS_DATA_CONN_DONE = 4;
137    private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
138
139    // these need to match GpsLocationFlags enum in gps.h
140    private static final int LOCATION_INVALID = 0;
141    private static final int LOCATION_HAS_LAT_LONG = 1;
142    private static final int LOCATION_HAS_ALTITUDE = 2;
143    private static final int LOCATION_HAS_SPEED = 4;
144    private static final int LOCATION_HAS_BEARING = 8;
145    private static final int LOCATION_HAS_ACCURACY = 16;
146
147    // IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
148    private static final int GPS_DELETE_EPHEMERIS = 0x0001;
149    private static final int GPS_DELETE_ALMANAC = 0x0002;
150    private static final int GPS_DELETE_POSITION = 0x0004;
151    private static final int GPS_DELETE_TIME = 0x0008;
152    private static final int GPS_DELETE_IONO = 0x0010;
153    private static final int GPS_DELETE_UTC = 0x0020;
154    private static final int GPS_DELETE_HEALTH = 0x0040;
155    private static final int GPS_DELETE_SVDIR = 0x0080;
156    private static final int GPS_DELETE_SVSTEER = 0x0100;
157    private static final int GPS_DELETE_SADATA = 0x0200;
158    private static final int GPS_DELETE_RTI = 0x0400;
159    private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
160    private static final int GPS_DELETE_ALL = 0xFFFF;
161
162    // The GPS_CAPABILITY_* flags must match the values in gps.h
163    private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
164    private static final int GPS_CAPABILITY_MSB = 0x0000002;
165    private static final int GPS_CAPABILITY_MSA = 0x0000004;
166    private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
167    private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
168    private static final int GPS_CAPABILITY_GEOFENCING = 0x0000020;
169    private static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
170    private static final int GPS_CAPABILITY_NAV_MESSAGES = 0x0000080;
171
172    // The AGPS SUPL mode
173    private static final int AGPS_SUPL_MODE_MSA = 0x02;
174    private static final int AGPS_SUPL_MODE_MSB = 0x01;
175
176    // these need to match AGpsType enum in gps.h
177    private static final int AGPS_TYPE_SUPL = 1;
178    private static final int AGPS_TYPE_C2K = 2;
179
180    // these must match the definitions in gps.h
181    private static final int APN_INVALID = 0;
182    private static final int APN_IPV4 = 1;
183    private static final int APN_IPV6 = 2;
184    private static final int APN_IPV4V6 = 3;
185
186    // for mAGpsDataConnectionState
187    private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
188    private static final int AGPS_DATA_CONNECTION_OPENING = 1;
189    private static final int AGPS_DATA_CONNECTION_OPEN = 2;
190
191    // Handler messages
192    private static final int CHECK_LOCATION = 1;
193    private static final int ENABLE = 2;
194    private static final int SET_REQUEST = 3;
195    private static final int UPDATE_NETWORK_STATE = 4;
196    private static final int INJECT_NTP_TIME = 5;
197    private static final int DOWNLOAD_XTRA_DATA = 6;
198    private static final int UPDATE_LOCATION = 7;
199    private static final int ADD_LISTENER = 8;
200    private static final int REMOVE_LISTENER = 9;
201    private static final int INJECT_NTP_TIME_FINISHED = 10;
202    private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
203    private static final int SUBSCRIPTION_OR_SIM_CHANGED = 12;
204    private static final int INITIALIZE_HANDLER = 13;
205    private static final int REQUEST_SUPL_CONNECTION = 14;
206    private static final int RELEASE_SUPL_CONNECTION = 15;
207
208    // Request setid
209    private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
210    private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
211
212    // Request ref location
213    private static final int AGPS_RIL_REQUEST_REFLOC_CELLID = 1;
214    private static final int AGPS_RIL_REQUEST_REFLOC_MAC = 2;
215
216    // ref. location info
217    private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
218    private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
219    private static final int AGPS_REG_LOCATION_TYPE_MAC        = 3;
220
221    // set id info
222    private static final int AGPS_SETID_TYPE_NONE = 0;
223    private static final int AGPS_SETID_TYPE_IMSI = 1;
224    private static final int AGPS_SETID_TYPE_MSISDN = 2;
225
226    private static final String PROPERTIES_FILE_PREFIX = "/etc/gps";
227    private static final String PROPERTIES_FILE_SUFFIX = ".conf";
228    private static final String DEFAULT_PROPERTIES_FILE = PROPERTIES_FILE_PREFIX + PROPERTIES_FILE_SUFFIX;
229
230    private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
231    private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
232
233    // GPS Geofence errors. Should match gps.h constants.
234    private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
235    private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
236    private static final int GPS_GEOFENCE_ERROR_ID_EXISTS  = -101;
237    private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
238    private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
239    private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
240
241    // TCP/IP constants.
242    // Valid TCP/UDP port range is (0, 65535].
243    private static final int TCP_MIN_PORT = 0;
244    private static final int TCP_MAX_PORT = 0xffff;
245
246    // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode.
247    private static final int BATTERY_SAVER_MODE_NO_CHANGE = 0;
248    // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
249    // is enabled and the screen is off.
250    private static final int BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF = 1;
251    // Secure setting for GPS behavior when battery saver mode is on.
252    private static final String BATTERY_SAVER_GPS_MODE = "batterySaverGpsMode";
253
254    /** simpler wrapper for ProviderRequest + Worksource */
255    private static class GpsRequest {
256        public ProviderRequest request;
257        public WorkSource source;
258        public GpsRequest(ProviderRequest request, WorkSource source) {
259            this.request = request;
260            this.source = source;
261        }
262    }
263
264    private Object mLock = new Object();
265
266    private int mLocationFlags = LOCATION_INVALID;
267
268    // current status
269    private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
270
271    // time for last status update
272    private long mStatusUpdateTime = SystemClock.elapsedRealtime();
273
274    // turn off GPS fix icon if we haven't received a fix in 10 seconds
275    private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
276
277    // stop trying if we do not receive a fix within 60 seconds
278    private static final int NO_FIX_TIMEOUT = 60 * 1000;
279
280    // if the fix interval is below this we leave GPS on,
281    // if above then we cycle the GPS driver.
282    // Typical hot TTTF is ~5 seconds, so 10 seconds seems sane.
283    private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
284
285    // how often to request NTP time, in milliseconds
286    // current setting 24 hours
287    private static final long NTP_INTERVAL = 24*60*60*1000;
288    // how long to wait if we have a network error in NTP or XTRA downloading
289    // the initial value of the exponential backoff
290    // current setting - 5 minutes
291    private static final long RETRY_INTERVAL = 5*60*1000;
292    // how long to wait if we have a network error in NTP or XTRA downloading
293    // the max value of the exponential backoff
294    // current setting - 4 hours
295    private static final long MAX_RETRY_INTERVAL = 4*60*60*1000;
296
297    private BackOff mNtpBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
298    private BackOff mXtraBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
299
300    // true if we are enabled, protected by this
301    private boolean mEnabled;
302
303    // states for injecting ntp and downloading xtra data
304    private static final int STATE_PENDING_NETWORK = 0;
305    private static final int STATE_DOWNLOADING = 1;
306    private static final int STATE_IDLE = 2;
307
308    // flags to trigger NTP or XTRA data download when network becomes available
309    // initialized to true so we do NTP and XTRA when the network comes up after booting
310    private int mInjectNtpTimePending = STATE_PENDING_NETWORK;
311    private int mDownloadXtraDataPending = STATE_PENDING_NETWORK;
312
313    // set to true if the GPS engine requested on-demand NTP time requests
314    private boolean mOnDemandTimeInjection;
315
316    // true if GPS is navigating
317    private boolean mNavigating;
318
319    // true if GPS engine is on
320    private boolean mEngineOn;
321
322    // requested frequency of fixes, in milliseconds
323    private int mFixInterval = 1000;
324
325    // true if we started navigation
326    private boolean mStarted;
327
328    // true if single shot request is in progress
329    private boolean mSingleShot;
330
331    // capabilities of the GPS engine
332    private int mEngineCapabilities;
333
334    // true if XTRA is supported
335    private boolean mSupportsXtra;
336
337    // for calculating time to first fix
338    private long mFixRequestTime = 0;
339    // time to first fix for most recent session
340    private int mTimeToFirstFix = 0;
341    // time we received our last fix
342    private long mLastFixTime;
343
344    private int mPositionMode;
345
346    // Current request from underlying location clients.
347    private ProviderRequest mProviderRequest = null;
348    // Current list of underlying location clients.
349    private WorkSource mWorkSource = null;
350    // True if gps should be disabled (used to support battery saver mode in settings).
351    private boolean mDisableGps = false;
352
353    /**
354     * Properties loaded from PROPERTIES_FILE.
355     * It must be accessed only inside {@link #mHandler}.
356     */
357    private Properties mProperties;
358
359    private String mSuplServerHost;
360    private int mSuplServerPort = TCP_MIN_PORT;
361    private String mC2KServerHost;
362    private int mC2KServerPort;
363    private boolean mSuplEsEnabled = false;
364
365    private final Context mContext;
366    private final NtpTrustedTime mNtpTime;
367    private final ILocationManager mILocationManager;
368    private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
369    private Bundle mLocationExtras = new Bundle();
370    private final GnssStatusListenerHelper mListenerHelper;
371    private final GnssMeasurementsProvider mGnssMeasurementsProvider;
372    private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
373
374    // Handler for processing events
375    private Handler mHandler;
376
377    /** It must be accessed only inside {@link #mHandler}. */
378    private int mAGpsDataConnectionState;
379    /** It must be accessed only inside {@link #mHandler}. */
380    private InetAddress mAGpsDataConnectionIpAddr;
381
382    private final ConnectivityManager mConnMgr;
383    private final GpsNetInitiatedHandler mNIHandler;
384
385    // Wakelocks
386    private final static String WAKELOCK_KEY = "GnssLocationProvider";
387    private final PowerManager.WakeLock mWakeLock;
388
389    // Alarms
390    private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
391    private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
392
393    // SIM/Carrier info.
394    private final static String SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
395
396    private final PowerManager mPowerManager;
397    private final AlarmManager mAlarmManager;
398    private final PendingIntent mWakeupIntent;
399    private final PendingIntent mTimeoutIntent;
400
401    private final IAppOpsService mAppOpsService;
402    private final IBatteryStats mBatteryStats;
403
404    // only modified on handler thread
405    private WorkSource mClientSource = new WorkSource();
406
407    private GeofenceHardwareImpl mGeofenceHardwareImpl;
408
409    private int mYearOfHardware = 0;
410
411    private final IGnssStatusProvider mGnssStatusProvider = new IGnssStatusProvider.Stub() {
412        @Override
413        public void registerGnssStatusCallback(IGnssStatusListener callback) {
414            mListenerHelper.addListener(callback);
415        }
416
417        @Override
418        public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
419            mListenerHelper.removeListener(callback);
420        }
421    };
422
423    public IGnssStatusProvider getGnssStatusProvider() {
424        return mGnssStatusProvider;
425    }
426
427    public IGpsGeofenceHardware getGpsGeofenceProxy() {
428        return mGpsGeofenceBinder;
429    }
430
431    public GnssMeasurementsProvider getGnssMeasurementsProvider() {
432        return mGnssMeasurementsProvider;
433    }
434
435    public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
436        return mGnssNavigationMessageProvider;
437    }
438
439    /**
440     * Callback used to listen for data connectivity changes.
441     */
442    private final ConnectivityManager.NetworkCallback mNetworkConnectivityCallback =
443            new ConnectivityManager.NetworkCallback() {
444        @Override
445        public void onAvailable(Network network) {
446            requestUtcTime();
447            xtraDownloadRequest();
448        }
449    };
450
451    /**
452     * Callback used to listen for availability of a requested SUPL connection.
453     * It is kept as a separate instance from {@link #mNetworkConnectivityCallback} to be able to
454     * manage the registration/un-registration lifetimes separate.
455     */
456    private final ConnectivityManager.NetworkCallback mSuplConnectivityCallback =
457            new ConnectivityManager.NetworkCallback() {
458        @Override
459        public void onAvailable(Network network) {
460            sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
461        }
462
463        @Override
464        public void onLost(Network network) {
465            releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
466        }
467
468        @Override
469        public void onUnavailable() {
470            // timeout, it was not possible to establish the required connection
471            releaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
472        }
473    };
474
475    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
476        @Override public void onReceive(Context context, Intent intent) {
477            String action = intent.getAction();
478            if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
479            if (action == null) {
480                return;
481            }
482
483            if (action.equals(ALARM_WAKEUP)) {
484                startNavigating(false);
485            } else if (action.equals(ALARM_TIMEOUT)) {
486                hibernate();
487            } else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
488                checkSmsSuplInit(intent);
489            } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
490                checkWapSuplInit(intent);
491            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
492                    || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
493                    || Intent.ACTION_SCREEN_OFF.equals(action)
494                    || Intent.ACTION_SCREEN_ON.equals(action)) {
495                updateLowPowerMode();
496            } else if (action.equals(SIM_STATE_CHANGED)) {
497                subscriptionOrSimChanged(context);
498            }
499        }
500    };
501
502    private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
503            new OnSubscriptionsChangedListener() {
504        @Override
505        public void onSubscriptionsChanged() {
506            sendMessage(SUBSCRIPTION_OR_SIM_CHANGED, 0, null);
507        }
508    };
509
510    private void subscriptionOrSimChanged(Context context) {
511        if (DEBUG) Log.d(TAG, "received SIM related action: ");
512        TelephonyManager phone = (TelephonyManager)
513                mContext.getSystemService(Context.TELEPHONY_SERVICE);
514        String mccMnc = phone.getSimOperator();
515        if (!TextUtils.isEmpty(mccMnc)) {
516            if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
517            synchronized (mLock) {
518                reloadGpsProperties(context, mProperties);
519                mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
520            }
521        } else {
522            if (DEBUG) Log.d(TAG, "SIM MCC/MNC is still not available");
523        }
524    }
525
526    private void checkSmsSuplInit(Intent intent) {
527        SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
528        if (messages == null) {
529            Log.e(TAG, "Message does not exist in the intent.");
530            return;
531        }
532
533        for (SmsMessage message : messages) {
534            if (message != null && message.mWrappedSmsMessage != null) {
535                byte[] suplInit = message.getUserData();
536                if (suplInit != null) {
537                    native_agps_ni_message(suplInit, suplInit.length);
538                }
539            }
540        }
541    }
542
543    private void checkWapSuplInit(Intent intent) {
544        byte[] suplInit = intent.getByteArrayExtra("data");
545        if (suplInit == null) {
546            return;
547        }
548        native_agps_ni_message(suplInit,suplInit.length);
549    }
550
551    private void updateLowPowerMode() {
552        // Disable GPS if we are in device idle mode.
553        boolean disableGps = mPowerManager.isDeviceIdleMode();
554        switch (Settings.Secure.getInt(mContext.getContentResolver(), BATTERY_SAVER_GPS_MODE,
555                BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF)) {
556            case BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF:
557                // If we are in battery saver mode and the screen is off, disable GPS.
558                disableGps |= mPowerManager.isPowerSaveMode() && !mPowerManager.isInteractive();
559                break;
560        }
561        if (disableGps != mDisableGps) {
562            mDisableGps = disableGps;
563            updateRequirements();
564        }
565    }
566
567    public static boolean isSupported() {
568        return native_is_supported();
569    }
570
571    private void reloadGpsProperties(Context context, Properties properties) {
572        if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
573        loadPropertiesFromResource(context, properties);
574        boolean isPropertiesLoadedFromFile = false;
575        final String gpsHardware = SystemProperties.get("ro.hardware.gps");
576        if (!TextUtils.isEmpty(gpsHardware)) {
577            final String propFilename =
578                    PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX;
579            isPropertiesLoadedFromFile =
580                    loadPropertiesFromFile(propFilename, properties);
581        }
582        if (!isPropertiesLoadedFromFile) {
583            loadPropertiesFromFile(DEFAULT_PROPERTIES_FILE, properties);
584        }
585        if (DEBUG) Log.d(TAG, "GPS properties reloaded, size = " + properties.size());
586
587        // TODO: we should get rid of C2K specific setting.
588        setSuplHostPort(properties.getProperty("SUPL_HOST"),
589                        properties.getProperty("SUPL_PORT"));
590        mC2KServerHost = properties.getProperty("C2K_HOST");
591        String portString = properties.getProperty("C2K_PORT");
592        if (mC2KServerHost != null && portString != null) {
593            try {
594                mC2KServerPort = Integer.parseInt(portString);
595            } catch (NumberFormatException e) {
596                Log.e(TAG, "unable to parse C2K_PORT: " + portString);
597            }
598        }
599
600        if (native_is_gnss_configuration_supported()) {
601            try {
602                // Convert properties to string contents and send it to HAL.
603                ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
604                properties.store(baos, null);
605                native_configuration_update(baos.toString());
606                if (DEBUG) Log.d(TAG, "final config = " + baos.toString());
607            } catch (IOException ex) {
608                Log.e(TAG, "failed to dump properties contents");
609            }
610        } else if (DEBUG) {
611            Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
612                    + " supported");
613        }
614
615        // SUPL_ES configuration.
616        String suplESProperty = mProperties.getProperty("SUPL_ES");
617        if (suplESProperty != null) {
618            try {
619                mSuplEsEnabled = (Integer.parseInt(suplESProperty) == 1);
620            } catch (NumberFormatException e) {
621                Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
622            }
623        }
624    }
625
626    private void loadPropertiesFromResource(Context context,
627                                            Properties properties) {
628        String[] configValues = context.getResources().getStringArray(
629                com.android.internal.R.array.config_gpsParameters);
630        for (String item : configValues) {
631            if (DEBUG) Log.d(TAG, "GpsParamsResource: " + item);
632            // We need to support "KEY =", but not "=VALUE".
633            String[] split = item.split("=");
634            if (split.length == 2) {
635                properties.setProperty(split[0].trim().toUpperCase(), split[1]);
636            } else {
637                Log.w(TAG, "malformed contents: " + item);
638            }
639        }
640    }
641
642    private boolean loadPropertiesFromFile(String filename,
643                                           Properties properties) {
644        try {
645            File file = new File(filename);
646            FileInputStream stream = null;
647            try {
648                stream = new FileInputStream(file);
649                properties.load(stream);
650            } finally {
651                IoUtils.closeQuietly(stream);
652            }
653
654        } catch (IOException e) {
655            Log.w(TAG, "Could not open GPS configuration file " + filename);
656            return false;
657        }
658        return true;
659    }
660
661    public GnssLocationProvider(Context context, ILocationManager ilocationManager,
662            Looper looper) {
663        mContext = context;
664        mNtpTime = NtpTrustedTime.getInstance(context);
665        mILocationManager = ilocationManager;
666
667        mLocation.setExtras(mLocationExtras);
668
669        // Create a wake lock
670        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
671        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
672        mWakeLock.setReferenceCounted(true);
673
674        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
675        mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
676        mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
677
678        mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
679
680        // App ops service to keep track of who is accessing the GPS
681        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
682                Context.APP_OPS_SERVICE));
683
684        // Battery statistics service to be notified when GPS turns on or off
685        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
686                BatteryStats.SERVICE_NAME));
687
688        // Construct internal handler
689        mHandler = new ProviderHandler(looper);
690
691        // Load GPS configuration and register listeners in the background:
692        // some operations, such as opening files and registering broadcast receivers, can take a
693        // relative long time, so the ctor() is kept to create objects needed by this instance,
694        // while IO initialization and registration is delegated to our internal handler
695        // this approach is just fine because events are posted to our handler anyway
696        mProperties = new Properties();
697        sendMessage(INITIALIZE_HANDLER, 0, null);
698
699        // Create a GPS net-initiated handler.
700        mNIHandler = new GpsNetInitiatedHandler(context,
701                                                mNetInitiatedListener,
702                                                mSuplEsEnabled);
703
704        mListenerHelper = new GnssStatusListenerHelper(mHandler) {
705            @Override
706            protected boolean isAvailableInPlatform() {
707                return isSupported();
708            }
709
710            @Override
711            protected boolean isGpsEnabled() {
712                return isEnabled();
713            }
714        };
715
716        mGnssMeasurementsProvider = new GnssMeasurementsProvider(mHandler) {
717            @Override
718            public boolean isAvailableInPlatform() {
719                return native_is_measurement_supported();
720            }
721
722            @Override
723            protected boolean registerWithService() {
724                return native_start_measurement_collection();
725            }
726
727            @Override
728            protected void unregisterFromService() {
729                native_stop_measurement_collection();
730            }
731
732            @Override
733            protected boolean isGpsEnabled() {
734                return isEnabled();
735            }
736        };
737
738        mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mHandler) {
739            @Override
740            protected boolean isAvailableInPlatform() {
741                return native_is_navigation_message_supported();
742            }
743
744            @Override
745            protected boolean registerWithService() {
746                return native_start_navigation_message_collection();
747            }
748
749            @Override
750            protected void unregisterFromService() {
751                native_stop_navigation_message_collection();
752            }
753
754            @Override
755            protected boolean isGpsEnabled() {
756                return isEnabled();
757            }
758        };
759    }
760
761    /**
762     * Returns the name of this provider.
763     */
764    @Override
765    public String getName() {
766        return LocationManager.GPS_PROVIDER;
767    }
768
769    @Override
770    public ProviderProperties getProperties() {
771        return PROPERTIES;
772    }
773
774    private void handleUpdateNetworkState(Network network) {
775        // retrieve NetworkInfo for this UID
776        NetworkInfo info = mConnMgr.getNetworkInfo(network);
777        if (info == null) {
778            return;
779        }
780
781        boolean isConnected = info.isConnected();
782        if (DEBUG) {
783            String message = String.format(
784                    "UpdateNetworkState, state=%s, connected=%s, info=%s, capabilities=%S",
785                    agpsDataConnStateAsString(),
786                    isConnected,
787                    info,
788                    mConnMgr.getNetworkCapabilities(network));
789            Log.d(TAG, message);
790        }
791
792        if (native_is_agps_ril_supported()) {
793            boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
794            boolean networkAvailable = info.isAvailable() && dataEnabled;
795            String defaultApn = getSelectedApn();
796            if (defaultApn == null) {
797                defaultApn = "dummy-apn";
798            }
799
800            native_update_network_state(
801                    isConnected,
802                    info.getType(),
803                    info.isRoaming(),
804                    networkAvailable,
805                    info.getExtraInfo(),
806                    defaultApn);
807        } else if (DEBUG) {
808            Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not  supported");
809        }
810
811        if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
812            if (isConnected) {
813                String apnName = info.getExtraInfo();
814                if (apnName == null) {
815                    // assign a dummy value in the case of C2K as otherwise we will have a runtime
816                    // exception in the following call to native_agps_data_conn_open
817                    apnName = "dummy-apn";
818                }
819                int apnIpType = getApnIpType(apnName);
820                setRouting();
821                if (DEBUG) {
822                    String message = String.format(
823                            "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
824                            apnName,
825                            apnIpType);
826                    Log.d(TAG, message);
827                }
828                native_agps_data_conn_open(apnName, apnIpType);
829                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
830            } else {
831                handleReleaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
832            }
833        }
834    }
835
836    private void handleRequestSuplConnection(InetAddress address) {
837        if (DEBUG) {
838            String message = String.format(
839                    "requestSuplConnection, state=%s, address=%s",
840                    agpsDataConnStateAsString(),
841                    address);
842            Log.d(TAG, message);
843        }
844
845        if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
846            return;
847        }
848        mAGpsDataConnectionIpAddr = address;
849        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
850
851        NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
852        requestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
853        requestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
854        NetworkRequest request = requestBuilder.build();
855        mConnMgr.requestNetwork(
856                request,
857                mSuplConnectivityCallback,
858                ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS);
859    }
860
861    private void handleReleaseSuplConnection(int agpsDataConnStatus) {
862        if (DEBUG) {
863            String message = String.format(
864                    "releaseSuplConnection, state=%s, status=%s",
865                    agpsDataConnStateAsString(),
866                    agpsDataConnStatusAsString(agpsDataConnStatus));
867            Log.d(TAG, message);
868        }
869
870        if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
871            return;
872        }
873        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
874
875        mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback);
876        switch (agpsDataConnStatus) {
877            case GPS_AGPS_DATA_CONN_FAILED:
878                native_agps_data_conn_failed();
879                break;
880            case GPS_RELEASE_AGPS_DATA_CONN:
881                native_agps_data_conn_closed();
882                break;
883            default:
884                Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus);
885        }
886    }
887
888    private void handleInjectNtpTime() {
889        if (mInjectNtpTimePending == STATE_DOWNLOADING) {
890            // already downloading data
891            return;
892        }
893        if (!isDataNetworkConnected()) {
894            // try again when network is up
895            mInjectNtpTimePending = STATE_PENDING_NETWORK;
896            return;
897        }
898        mInjectNtpTimePending = STATE_DOWNLOADING;
899
900        // hold wake lock while task runs
901        mWakeLock.acquire();
902        Log.i(TAG, "WakeLock acquired by handleInjectNtpTime()");
903        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
904            @Override
905            public void run() {
906                long delay;
907
908                // force refresh NTP cache when outdated
909                boolean refreshSuccess = true;
910                if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
911                    refreshSuccess = mNtpTime.forceRefresh();
912                }
913
914                // only update when NTP time is fresh
915                if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
916                    long time = mNtpTime.getCachedNtpTime();
917                    long timeReference = mNtpTime.getCachedNtpTimeReference();
918                    long certainty = mNtpTime.getCacheCertainty();
919                    long now = System.currentTimeMillis();
920
921                    if (DEBUG) {
922                        Log.d(TAG, "NTP server returned: "
923                                + time + " (" + new Date(time)
924                                + ") reference: " + timeReference
925                                + " certainty: " + certainty
926                                + " system time offset: " + (time - now));
927                    }
928
929                    native_inject_time(time, timeReference, (int) certainty);
930                    delay = NTP_INTERVAL;
931                    mNtpBackOff.reset();
932                } else {
933                    Log.e(TAG, "requestTime failed");
934                    delay = mNtpBackOff.nextBackoffMillis();
935                }
936
937                sendMessage(INJECT_NTP_TIME_FINISHED, 0, null);
938
939                if (DEBUG) {
940                    String message = String.format(
941                            "onDemandTimeInjection=%s, refreshSuccess=%s, delay=%s",
942                            mOnDemandTimeInjection,
943                            refreshSuccess,
944                            delay);
945                    Log.d(TAG, message);
946                }
947                if (mOnDemandTimeInjection || !refreshSuccess) {
948                    // send delayed message for next NTP injection
949                    // since this is delayed and not urgent we do not hold a wake lock here
950                    mHandler.sendEmptyMessageDelayed(INJECT_NTP_TIME, delay);
951                }
952
953                // release wake lock held by task
954                mWakeLock.release();
955                Log.i(TAG, "WakeLock released by handleInjectNtpTime()");
956            }
957        });
958    }
959
960    private void handleDownloadXtraData() {
961        if (mDownloadXtraDataPending == STATE_DOWNLOADING) {
962            // already downloading data
963            return;
964        }
965        if (!isDataNetworkConnected()) {
966            // try again when network is up
967            mDownloadXtraDataPending = STATE_PENDING_NETWORK;
968            return;
969        }
970        mDownloadXtraDataPending = STATE_DOWNLOADING;
971
972        // hold wake lock while task runs
973        mWakeLock.acquire();
974        Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
975        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
976            @Override
977            public void run() {
978                GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
979                byte[] data = xtraDownloader.downloadXtraData();
980                if (data != null) {
981                    if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
982                    native_inject_xtra_data(data, data.length);
983                    mXtraBackOff.reset();
984                }
985
986                sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
987
988                if (data == null) {
989                    // try again later
990                    // since this is delayed and not urgent we do not hold a wake lock here
991                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
992                            mXtraBackOff.nextBackoffMillis());
993                }
994
995                // release wake lock held by task
996                mWakeLock.release();
997                Log.i(TAG, "WakeLock released by handleDownloadXtraData()");
998            }
999        });
1000    }
1001
1002    private void handleUpdateLocation(Location location) {
1003        if (location.hasAccuracy()) {
1004            native_inject_location(location.getLatitude(), location.getLongitude(),
1005                    location.getAccuracy());
1006        }
1007    }
1008
1009    /**
1010     * Enables this provider.  When enabled, calls to getStatus()
1011     * must be handled.  Hardware may be started up
1012     * when the provider is enabled.
1013     */
1014    @Override
1015    public void enable() {
1016        synchronized (mLock) {
1017            if (mEnabled) return;
1018            mEnabled = true;
1019        }
1020
1021        sendMessage(ENABLE, 1, null);
1022    }
1023
1024    private void setSuplHostPort(String hostString, String portString) {
1025        if (hostString != null) {
1026            mSuplServerHost = hostString;
1027        }
1028        if (portString != null) {
1029            try {
1030                mSuplServerPort = Integer.parseInt(portString);
1031            } catch (NumberFormatException e) {
1032                Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
1033            }
1034        }
1035        if (mSuplServerHost != null
1036                && mSuplServerPort > TCP_MIN_PORT
1037                && mSuplServerPort <= TCP_MAX_PORT) {
1038            native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
1039        }
1040    }
1041
1042    /**
1043     * Checks what SUPL mode to use, according to the AGPS mode as well as the
1044     * allowed mode from properties.
1045     *
1046     * @param properties GPS properties
1047     * @param agpsEnabled whether AGPS is enabled by settings value
1048     * @param singleShot whether "singleshot" is needed
1049     * @return SUPL mode (MSA vs MSB vs STANDALONE)
1050     */
1051    private int getSuplMode(Properties properties, boolean agpsEnabled, boolean singleShot) {
1052        if (agpsEnabled) {
1053            String modeString = properties.getProperty("SUPL_MODE");
1054            int suplMode = 0;
1055            if (!TextUtils.isEmpty(modeString)) {
1056                try {
1057                    suplMode = Integer.parseInt(modeString);
1058                } catch (NumberFormatException e) {
1059                    Log.e(TAG, "unable to parse SUPL_MODE: " + modeString);
1060                    return GPS_POSITION_MODE_STANDALONE;
1061                }
1062            }
1063            // MS-Based is the preferred mode for Assisted-GPS position computation, so we favor
1064            // such mode when it is available
1065            if (hasCapability(GPS_CAPABILITY_MSB) && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
1066                return GPS_POSITION_MODE_MS_BASED;
1067            }
1068            // for now, just as the legacy code did, we fallback to MS-Assisted if it is available,
1069            // do fallback only for single-shot requests, because it is too expensive to do for
1070            // periodic requests as well
1071            if (singleShot
1072                    && hasCapability(GPS_CAPABILITY_MSA)
1073                    && (suplMode & AGPS_SUPL_MODE_MSA) != 0) {
1074                return GPS_POSITION_MODE_MS_ASSISTED;
1075            }
1076        }
1077        return GPS_POSITION_MODE_STANDALONE;
1078    }
1079
1080    private void handleEnable() {
1081        if (DEBUG) Log.d(TAG, "handleEnable");
1082
1083        boolean enabled = native_init();
1084
1085        if (enabled) {
1086            mSupportsXtra = native_supports_xtra();
1087
1088            // TODO: remove the following native calls if we can make sure they are redundant.
1089            if (mSuplServerHost != null) {
1090                native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
1091            }
1092            if (mC2KServerHost != null) {
1093                native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
1094            }
1095
1096            mGnssMeasurementsProvider.onGpsEnabledChanged();
1097            mGnssNavigationMessageProvider.onGpsEnabledChanged();
1098        } else {
1099            synchronized (mLock) {
1100                mEnabled = false;
1101            }
1102            Log.w(TAG, "Failed to enable location provider");
1103        }
1104    }
1105
1106    /**
1107     * Disables this provider.  When disabled, calls to getStatus()
1108     * need not be handled.  Hardware may be shut
1109     * down while the provider is disabled.
1110     */
1111    @Override
1112    public void disable() {
1113        synchronized (mLock) {
1114            if (!mEnabled) return;
1115            mEnabled = false;
1116        }
1117
1118        sendMessage(ENABLE, 0, null);
1119    }
1120
1121    private void handleDisable() {
1122        if (DEBUG) Log.d(TAG, "handleDisable");
1123
1124        updateClientUids(new WorkSource());
1125        stopNavigating();
1126        mAlarmManager.cancel(mWakeupIntent);
1127        mAlarmManager.cancel(mTimeoutIntent);
1128
1129        // do this before releasing wakelock
1130        native_cleanup();
1131
1132        mGnssMeasurementsProvider.onGpsEnabledChanged();
1133        mGnssNavigationMessageProvider.onGpsEnabledChanged();
1134    }
1135
1136    @Override
1137    public boolean isEnabled() {
1138        synchronized (mLock) {
1139            return mEnabled;
1140        }
1141    }
1142
1143    @Override
1144    public int getStatus(Bundle extras) {
1145        if (extras != null) {
1146            extras.putInt("satellites", mSvCount);
1147        }
1148        return mStatus;
1149    }
1150
1151    private void updateStatus(int status, int svCount) {
1152        if (status != mStatus || svCount != mSvCount) {
1153            mStatus = status;
1154            mSvCount = svCount;
1155            mLocationExtras.putInt("satellites", svCount);
1156            mStatusUpdateTime = SystemClock.elapsedRealtime();
1157        }
1158    }
1159
1160    @Override
1161    public long getStatusUpdateTime() {
1162        return mStatusUpdateTime;
1163    }
1164
1165    @Override
1166    public void setRequest(ProviderRequest request, WorkSource source) {
1167        sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
1168    }
1169
1170    private void handleSetRequest(ProviderRequest request, WorkSource source) {
1171        mProviderRequest = request;
1172        mWorkSource = source;
1173        updateRequirements();
1174    }
1175
1176    // Called when the requirements for GPS may have changed
1177    private void updateRequirements() {
1178        if (mProviderRequest == null || mWorkSource == null) {
1179            return;
1180        }
1181
1182        boolean singleShot = false;
1183
1184        // see if the request is for a single update
1185        if (mProviderRequest.locationRequests != null
1186                && mProviderRequest.locationRequests.size() > 0) {
1187            // if any request has zero or more than one updates
1188            // requested, then this is not single-shot mode
1189            singleShot = true;
1190
1191            for (LocationRequest lr : mProviderRequest.locationRequests) {
1192                if (lr.getNumUpdates() != 1) {
1193                    singleShot = false;
1194                }
1195            }
1196        }
1197
1198        if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
1199        if (mProviderRequest.reportLocation && !mDisableGps && isEnabled()) {
1200            // update client uids
1201            updateClientUids(mWorkSource);
1202
1203            mFixInterval = (int) mProviderRequest.interval;
1204
1205            // check for overflow
1206            if (mFixInterval != mProviderRequest.interval) {
1207                Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
1208                mFixInterval = Integer.MAX_VALUE;
1209            }
1210
1211            // apply request to GPS engine
1212            if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
1213                // change period
1214                if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
1215                        mFixInterval, 0, 0)) {
1216                    Log.e(TAG, "set_position_mode failed in setMinTime()");
1217                }
1218            } else if (!mStarted) {
1219                // start GPS
1220                startNavigating(singleShot);
1221            }
1222        } else {
1223            updateClientUids(new WorkSource());
1224
1225            stopNavigating();
1226            mAlarmManager.cancel(mWakeupIntent);
1227            mAlarmManager.cancel(mTimeoutIntent);
1228        }
1229    }
1230
1231    private void updateClientUids(WorkSource source) {
1232        // Update work source.
1233        WorkSource[] changes = mClientSource.setReturningDiffs(source);
1234        if (changes == null) {
1235            return;
1236        }
1237        WorkSource newWork = changes[0];
1238        WorkSource goneWork = changes[1];
1239
1240        // Update sources that were not previously tracked.
1241        if (newWork != null) {
1242            int lastuid = -1;
1243            for (int i=0; i<newWork.size(); i++) {
1244                try {
1245                    int uid = newWork.get(i);
1246                    mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
1247                            AppOpsManager.OP_GPS, uid, newWork.getName(i));
1248                    if (uid != lastuid) {
1249                        lastuid = uid;
1250                        mBatteryStats.noteStartGps(uid);
1251                    }
1252                } catch (RemoteException e) {
1253                    Log.w(TAG, "RemoteException", e);
1254                }
1255            }
1256        }
1257
1258        // Update sources that are no longer tracked.
1259        if (goneWork != null) {
1260            int lastuid = -1;
1261            for (int i=0; i<goneWork.size(); i++) {
1262                try {
1263                    int uid = goneWork.get(i);
1264                    mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
1265                            AppOpsManager.OP_GPS, uid, goneWork.getName(i));
1266                    if (uid != lastuid) {
1267                        lastuid = uid;
1268                        mBatteryStats.noteStopGps(uid);
1269                    }
1270                } catch (RemoteException e) {
1271                    Log.w(TAG, "RemoteException", e);
1272                }
1273            }
1274        }
1275    }
1276
1277    @Override
1278    public boolean sendExtraCommand(String command, Bundle extras) {
1279
1280        long identity = Binder.clearCallingIdentity();
1281        boolean result = false;
1282
1283        if ("delete_aiding_data".equals(command)) {
1284            result = deleteAidingData(extras);
1285        } else if ("force_time_injection".equals(command)) {
1286            requestUtcTime();
1287            result = true;
1288        } else if ("force_xtra_injection".equals(command)) {
1289            if (mSupportsXtra) {
1290                xtraDownloadRequest();
1291                result = true;
1292            }
1293        } else {
1294            Log.w(TAG, "sendExtraCommand: unknown command " + command);
1295        }
1296
1297        Binder.restoreCallingIdentity(identity);
1298        return result;
1299    }
1300
1301    private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
1302        public boolean isHardwareGeofenceSupported() {
1303            return native_is_geofence_supported();
1304        }
1305
1306        public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
1307                double longitude, double radius, int lastTransition, int monitorTransitions,
1308                int notificationResponsiveness, int unknownTimer) {
1309            return native_add_geofence(geofenceId, latitude, longitude, radius,
1310                    lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer);
1311        }
1312
1313        public boolean removeHardwareGeofence(int geofenceId) {
1314            return native_remove_geofence(geofenceId);
1315        }
1316
1317        public boolean pauseHardwareGeofence(int geofenceId) {
1318            return native_pause_geofence(geofenceId);
1319        }
1320
1321        public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) {
1322            return native_resume_geofence(geofenceId, monitorTransition);
1323        }
1324    };
1325
1326    private boolean deleteAidingData(Bundle extras) {
1327        int flags;
1328
1329        if (extras == null) {
1330            flags = GPS_DELETE_ALL;
1331        } else {
1332            flags = 0;
1333            if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
1334            if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
1335            if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
1336            if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
1337            if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
1338            if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
1339            if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
1340            if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
1341            if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
1342            if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
1343            if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
1344            if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
1345            if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
1346        }
1347
1348        if (flags != 0) {
1349            native_delete_aiding_data(flags);
1350            return true;
1351        }
1352
1353        return false;
1354    }
1355
1356    private void startNavigating(boolean singleShot) {
1357        if (!mStarted) {
1358            if (DEBUG) Log.d(TAG, "startNavigating, singleShot is " + singleShot);
1359            mTimeToFirstFix = 0;
1360            mLastFixTime = 0;
1361            mStarted = true;
1362            mSingleShot = singleShot;
1363            mPositionMode = GPS_POSITION_MODE_STANDALONE;
1364
1365            boolean agpsEnabled =
1366                    (Settings.Global.getInt(mContext.getContentResolver(),
1367                                            Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
1368            mPositionMode = getSuplMode(mProperties, agpsEnabled, singleShot);
1369
1370            if (DEBUG) {
1371                String mode;
1372
1373                switch(mPositionMode) {
1374                    case GPS_POSITION_MODE_STANDALONE:
1375                        mode = "standalone";
1376                        break;
1377                    case GPS_POSITION_MODE_MS_ASSISTED:
1378                        mode = "MS_ASSISTED";
1379                        break;
1380                    case GPS_POSITION_MODE_MS_BASED:
1381                        mode = "MS_BASED";
1382                        break;
1383                    default:
1384                        mode = "unknown";
1385                        break;
1386                }
1387                Log.d(TAG, "setting position_mode to " + mode);
1388            }
1389
1390            int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
1391            if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
1392                    interval, 0, 0)) {
1393                mStarted = false;
1394                Log.e(TAG, "set_position_mode failed in startNavigating()");
1395                return;
1396            }
1397            if (!native_start()) {
1398                mStarted = false;
1399                Log.e(TAG, "native_start failed in startNavigating()");
1400                return;
1401            }
1402
1403            // reset SV count to zero
1404            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
1405            mFixRequestTime = System.currentTimeMillis();
1406            if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
1407                // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
1408                // and our fix interval is not short
1409                if (mFixInterval >= NO_FIX_TIMEOUT) {
1410                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1411                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
1412                }
1413            }
1414        }
1415    }
1416
1417    private void stopNavigating() {
1418        if (DEBUG) Log.d(TAG, "stopNavigating");
1419        if (mStarted) {
1420            mStarted = false;
1421            mSingleShot = false;
1422            native_stop();
1423            mTimeToFirstFix = 0;
1424            mLastFixTime = 0;
1425            mLocationFlags = LOCATION_INVALID;
1426
1427            // reset SV count to zero
1428            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
1429        }
1430    }
1431
1432    private void hibernate() {
1433        // stop GPS until our next fix interval arrives
1434        stopNavigating();
1435        mAlarmManager.cancel(mTimeoutIntent);
1436        mAlarmManager.cancel(mWakeupIntent);
1437        long now = SystemClock.elapsedRealtime();
1438        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent);
1439    }
1440
1441    private boolean hasCapability(int capability) {
1442        return ((mEngineCapabilities & capability) != 0);
1443    }
1444
1445
1446    /**
1447     * called from native code to update our position.
1448     */
1449    private void reportLocation(int flags, double latitude, double longitude, double altitude,
1450            float speed, float bearing, float accuracy, long timestamp) {
1451        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
1452                " timestamp: " + timestamp);
1453
1454        synchronized (mLocation) {
1455            mLocationFlags = flags;
1456            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1457                mLocation.setLatitude(latitude);
1458                mLocation.setLongitude(longitude);
1459                mLocation.setTime(timestamp);
1460                // It would be nice to push the elapsed real-time timestamp
1461                // further down the stack, but this is still useful
1462                mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1463            }
1464            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1465                mLocation.setAltitude(altitude);
1466            } else {
1467                mLocation.removeAltitude();
1468            }
1469            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1470                mLocation.setSpeed(speed);
1471            } else {
1472                mLocation.removeSpeed();
1473            }
1474            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1475                mLocation.setBearing(bearing);
1476            } else {
1477                mLocation.removeBearing();
1478            }
1479            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1480                mLocation.setAccuracy(accuracy);
1481            } else {
1482                mLocation.removeAccuracy();
1483            }
1484            mLocation.setExtras(mLocationExtras);
1485
1486            try {
1487                mILocationManager.reportLocation(mLocation, false);
1488            } catch (RemoteException e) {
1489                Log.e(TAG, "RemoteException calling reportLocation");
1490            }
1491        }
1492
1493        mLastFixTime = System.currentTimeMillis();
1494        // report time to first fix
1495        if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1496            mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
1497            if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
1498
1499            // notify status listeners
1500            mListenerHelper.onFirstFix(mTimeToFirstFix);
1501        }
1502
1503        if (mSingleShot) {
1504            stopNavigating();
1505        }
1506
1507        if (mStarted && mStatus != LocationProvider.AVAILABLE) {
1508            // we want to time out if we do not receive a fix
1509            // within the time out and we are requesting infrequent fixes
1510            if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
1511                mAlarmManager.cancel(mTimeoutIntent);
1512            }
1513
1514            // send an intent to notify that the GPS is receiving fixes.
1515            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1516            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
1517            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1518            updateStatus(LocationProvider.AVAILABLE, mSvCount);
1519        }
1520
1521       if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
1522               mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
1523            if (DEBUG) Log.d(TAG, "got fix, hibernating");
1524            hibernate();
1525        }
1526   }
1527
1528    /**
1529     * called from native code to update our status
1530     */
1531    private void reportStatus(int status) {
1532        if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
1533
1534        boolean wasNavigating = mNavigating;
1535        switch (status) {
1536            case GPS_STATUS_SESSION_BEGIN:
1537                mNavigating = true;
1538                mEngineOn = true;
1539                break;
1540            case GPS_STATUS_SESSION_END:
1541                mNavigating = false;
1542                break;
1543            case GPS_STATUS_ENGINE_ON:
1544                mEngineOn = true;
1545                break;
1546            case GPS_STATUS_ENGINE_OFF:
1547                mEngineOn = false;
1548                mNavigating = false;
1549                break;
1550        }
1551
1552        if (wasNavigating != mNavigating) {
1553            mListenerHelper.onStatusChanged(mNavigating);
1554
1555            // send an intent to notify that the GPS has been enabled or disabled
1556            Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
1557            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
1558            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1559        }
1560    }
1561
1562    /**
1563     * called from native code to update SV info
1564     */
1565    private void reportSvStatus() {
1566        int svCount = native_read_sv_status(mSvidWithFlags, mCn0s, mSvElevations, mSvAzimuths);
1567        mListenerHelper.onSvStatusChanged(
1568                svCount,
1569                mSvidWithFlags,
1570                mCn0s,
1571                mSvElevations,
1572                mSvAzimuths);
1573
1574        if (VERBOSE) {
1575            Log.v(TAG, "SV count: " + svCount);
1576        }
1577        // Calculate number of sets used in fix.
1578        int usedInFixCount = 0;
1579        for (int i = 0; i < svCount; i++) {
1580            if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
1581                ++usedInFixCount;
1582            }
1583            if (VERBOSE) {
1584                Log.v(TAG, "svid: " + (mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
1585                        " cn0: " + mCn0s[i]/10 +
1586                        " elev: " + mSvElevations[i] +
1587                        " azimuth: " + mSvAzimuths[i] +
1588                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
1589                                ? "  " : " E") +
1590                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
1591                                ? "  " : " A") +
1592                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
1593                                ? "" : "U"));
1594            }
1595        }
1596        // return number of sets used in fix instead of total
1597        updateStatus(mStatus, usedInFixCount);
1598
1599        if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
1600            System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
1601            // send an intent to notify that the GPS is no longer receiving fixes.
1602            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1603            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
1604            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1605            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
1606        }
1607    }
1608
1609    /**
1610     * called from native code to update AGPS status
1611     */
1612    private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
1613        switch (status) {
1614            case GPS_REQUEST_AGPS_DATA_CONN:
1615                if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
1616                Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(ipaddr));
1617                InetAddress connectionIpAddress = null;
1618                if (ipaddr != null) {
1619                    try {
1620                        connectionIpAddress = InetAddress.getByAddress(ipaddr);
1621                        if (DEBUG) Log.d(TAG, "IP address converted to: " + connectionIpAddress);
1622                    } catch (UnknownHostException e) {
1623                        Log.e(TAG, "Bad IP Address: " + ipaddr, e);
1624                    }
1625                }
1626                sendMessage(REQUEST_SUPL_CONNECTION, 0 /*arg*/, connectionIpAddress);
1627                break;
1628            case GPS_RELEASE_AGPS_DATA_CONN:
1629                if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
1630                releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
1631                break;
1632            case GPS_AGPS_DATA_CONNECTED:
1633                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
1634                break;
1635            case GPS_AGPS_DATA_CONN_DONE:
1636                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
1637                break;
1638            case GPS_AGPS_DATA_CONN_FAILED:
1639                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
1640                break;
1641            default:
1642                if (DEBUG) Log.d(TAG, "Received Unknown AGPS status: " + status);
1643        }
1644    }
1645
1646    private void releaseSuplConnection(int connStatus) {
1647        sendMessage(RELEASE_SUPL_CONNECTION, connStatus, null /*obj*/);
1648    }
1649
1650    /**
1651     * called from native code to report NMEA data received
1652     */
1653    private void reportNmea(long timestamp) {
1654        int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
1655        String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
1656        mListenerHelper.onNmeaReceived(timestamp, nmea);
1657    }
1658
1659    /**
1660     * called from native code - Gps measurements callback
1661     */
1662    private void reportMeasurementData(GnssMeasurementsEvent event) {
1663        mGnssMeasurementsProvider.onMeasurementsAvailable(event);
1664    }
1665
1666    /**
1667     * called from native code - GPS navigation message callback
1668     */
1669    private void reportNavigationMessage(GnssNavigationMessage event) {
1670        mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
1671    }
1672
1673    /**
1674     * called from native code to inform us what the GPS engine capabilities are
1675     */
1676    private void setEngineCapabilities(int capabilities) {
1677        mEngineCapabilities = capabilities;
1678
1679        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
1680            mOnDemandTimeInjection = true;
1681            requestUtcTime();
1682        }
1683
1684        mGnssMeasurementsProvider.onCapabilitiesUpdated(
1685                (capabilities & GPS_CAPABILITY_MEASUREMENTS) == GPS_CAPABILITY_MEASUREMENTS);
1686        mGnssNavigationMessageProvider.onCapabilitiesUpdated(
1687                (capabilities & GPS_CAPABILITY_NAV_MESSAGES) == GPS_CAPABILITY_NAV_MESSAGES);
1688    }
1689
1690    /**
1691     * Called from native code to inform us the hardware information.
1692     */
1693    private void setGnssYearOfHardware(int yearOfHardware) {
1694        if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
1695        mYearOfHardware = yearOfHardware;
1696    }
1697
1698    public interface GnssSystemInfoProvider {
1699        /**
1700         * Returns the year of GPS hardware.
1701         */
1702        int getGnssYearOfHardware();
1703    }
1704
1705    /**
1706     * @hide
1707     */
1708    public GnssSystemInfoProvider getGnssSystemInfoProvider() {
1709        return new GnssSystemInfoProvider() {
1710            @Override
1711            public int getGnssYearOfHardware() {
1712                return mYearOfHardware;
1713            }
1714        };
1715    }
1716
1717    /**
1718     * called from native code to request XTRA data
1719     */
1720    private void xtraDownloadRequest() {
1721        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
1722        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
1723    }
1724
1725    /**
1726     * Helper method to construct a location object.
1727     */
1728    private Location buildLocation(
1729            int flags,
1730            double latitude,
1731            double longitude,
1732            double altitude,
1733            float speed,
1734            float bearing,
1735            float accuracy,
1736            long timestamp) {
1737        Location location = new Location(LocationManager.GPS_PROVIDER);
1738        if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1739            location.setLatitude(latitude);
1740            location.setLongitude(longitude);
1741            location.setTime(timestamp);
1742            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1743        }
1744        if((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1745            location.setAltitude(altitude);
1746        }
1747        if((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1748            location.setSpeed(speed);
1749        }
1750        if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1751            location.setBearing(bearing);
1752        }
1753        if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1754            location.setAccuracy(accuracy);
1755        }
1756        return location;
1757    }
1758
1759    /**
1760     * Converts the GPS HAL status to the internal Geofence Hardware status.
1761     */
1762    private int getGeofenceStatus(int status) {
1763        switch(status) {
1764            case GPS_GEOFENCE_OPERATION_SUCCESS:
1765                return GeofenceHardware.GEOFENCE_SUCCESS;
1766            case GPS_GEOFENCE_ERROR_GENERIC:
1767                return GeofenceHardware.GEOFENCE_FAILURE;
1768            case GPS_GEOFENCE_ERROR_ID_EXISTS:
1769                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
1770            case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
1771                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
1772            case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
1773                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
1774            case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
1775                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
1776            default:
1777                return -1;
1778        }
1779    }
1780
1781    /**
1782     * Called from native to report GPS Geofence transition
1783     * All geofence callbacks are called on the same thread
1784     */
1785    private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
1786            double longitude, double altitude, float speed, float bearing, float accuracy,
1787            long timestamp, int transition, long transitionTimestamp) {
1788        if (mGeofenceHardwareImpl == null) {
1789            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1790        }
1791        Location location = buildLocation(
1792                flags,
1793                latitude,
1794                longitude,
1795                altitude,
1796                speed,
1797                bearing,
1798                accuracy,
1799                timestamp);
1800        mGeofenceHardwareImpl.reportGeofenceTransition(
1801                geofenceId,
1802                location,
1803                transition,
1804                transitionTimestamp,
1805                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1806                FusedBatchOptions.SourceTechnologies.GNSS);
1807    }
1808
1809    /**
1810     * called from native code to report GPS status change.
1811     */
1812    private void reportGeofenceStatus(int status, int flags, double latitude,
1813            double longitude, double altitude, float speed, float bearing, float accuracy,
1814            long timestamp) {
1815        if (mGeofenceHardwareImpl == null) {
1816            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1817        }
1818        Location location = buildLocation(
1819                flags,
1820                latitude,
1821                longitude,
1822                altitude,
1823                speed,
1824                bearing,
1825                accuracy,
1826                timestamp);
1827        int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
1828        if(status == GPS_GEOFENCE_AVAILABLE) {
1829            monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
1830        }
1831        mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
1832                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1833                monitorStatus,
1834                location,
1835                FusedBatchOptions.SourceTechnologies.GNSS);
1836    }
1837
1838    /**
1839     * called from native code - Geofence Add callback
1840     */
1841    private void reportGeofenceAddStatus(int geofenceId, int status) {
1842        if (mGeofenceHardwareImpl == null) {
1843            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1844        }
1845        mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
1846    }
1847
1848    /**
1849     * called from native code - Geofence Remove callback
1850     */
1851    private void reportGeofenceRemoveStatus(int geofenceId, int status) {
1852        if (mGeofenceHardwareImpl == null) {
1853            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1854        }
1855        mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
1856    }
1857
1858    /**
1859     * called from native code - Geofence Pause callback
1860     */
1861    private void reportGeofencePauseStatus(int geofenceId, int status) {
1862        if (mGeofenceHardwareImpl == null) {
1863            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1864        }
1865        mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
1866    }
1867
1868    /**
1869     * called from native code - Geofence Resume callback
1870     */
1871    private void reportGeofenceResumeStatus(int geofenceId, int status) {
1872        if (mGeofenceHardwareImpl == null) {
1873            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1874        }
1875        mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
1876    }
1877
1878    //=============================================================
1879    // NI Client support
1880    //=============================================================
1881    private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
1882        // Sends a response for an NI request to HAL.
1883        @Override
1884        public boolean sendNiResponse(int notificationId, int userResponse)
1885        {
1886            // TODO Add Permission check
1887
1888            if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
1889                    ", response: " + userResponse);
1890            native_send_ni_response(notificationId, userResponse);
1891            return true;
1892        }
1893    };
1894
1895    public INetInitiatedListener getNetInitiatedListener() {
1896        return mNetInitiatedListener;
1897    }
1898
1899    // Called by JNI function to report an NI request.
1900    public void reportNiNotification(
1901            int notificationId,
1902            int niType,
1903            int notifyFlags,
1904            int timeout,
1905            int defaultResponse,
1906            String requestorId,
1907            String text,
1908            int requestorIdEncoding,
1909            int textEncoding,
1910            String extras  // Encoded extra data
1911        )
1912    {
1913        Log.i(TAG, "reportNiNotification: entered");
1914        Log.i(TAG, "notificationId: " + notificationId +
1915                ", niType: " + niType +
1916                ", notifyFlags: " + notifyFlags +
1917                ", timeout: " + timeout +
1918                ", defaultResponse: " + defaultResponse);
1919
1920        Log.i(TAG, "requestorId: " + requestorId +
1921                ", text: " + text +
1922                ", requestorIdEncoding: " + requestorIdEncoding +
1923                ", textEncoding: " + textEncoding);
1924
1925        GpsNiNotification notification = new GpsNiNotification();
1926
1927        notification.notificationId = notificationId;
1928        notification.niType = niType;
1929        notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
1930        notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
1931        notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
1932        notification.timeout = timeout;
1933        notification.defaultResponse = defaultResponse;
1934        notification.requestorId = requestorId;
1935        notification.text = text;
1936        notification.requestorIdEncoding = requestorIdEncoding;
1937        notification.textEncoding = textEncoding;
1938
1939        // Process extras, assuming the format is
1940        // one of more lines of "key = value"
1941        Bundle bundle = new Bundle();
1942
1943        if (extras == null) extras = "";
1944        Properties extraProp = new Properties();
1945
1946        try {
1947            extraProp.load(new StringReader(extras));
1948        }
1949        catch (IOException e)
1950        {
1951            Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
1952        }
1953
1954        for (Entry<Object, Object> ent : extraProp.entrySet())
1955        {
1956            bundle.putString((String) ent.getKey(), (String) ent.getValue());
1957        }
1958
1959        notification.extras = bundle;
1960
1961        mNIHandler.handleNiNotification(notification);
1962    }
1963
1964    /**
1965     * Called from native code to request set id info.
1966     * We should be careful about receiving null string from the TelephonyManager,
1967     * because sending null String to JNI function would cause a crash.
1968     */
1969
1970    private void requestSetID(int flags) {
1971        TelephonyManager phone = (TelephonyManager)
1972                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1973        int type = AGPS_SETID_TYPE_NONE;
1974        String data = "";
1975
1976        if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
1977            String data_temp = phone.getSubscriberId();
1978            if (data_temp == null) {
1979                // This means the framework does not have the SIM card ready.
1980            } else {
1981                // This means the framework has the SIM card.
1982                data = data_temp;
1983                type = AGPS_SETID_TYPE_IMSI;
1984            }
1985        }
1986        else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
1987            String data_temp = phone.getLine1Number();
1988            if (data_temp == null) {
1989                // This means the framework does not have the SIM card ready.
1990            } else {
1991                // This means the framework has the SIM card.
1992                data = data_temp;
1993                type = AGPS_SETID_TYPE_MSISDN;
1994            }
1995        }
1996        native_agps_set_id(type, data);
1997    }
1998
1999    /**
2000     * Called from native code to request utc time info
2001     */
2002    private void requestUtcTime() {
2003        if (DEBUG) Log.d(TAG, "utcTimeRequest");
2004        sendMessage(INJECT_NTP_TIME, 0, null);
2005    }
2006
2007    /**
2008     * Called from native code to request reference location info
2009     */
2010
2011    private void requestRefLocation(int flags) {
2012        TelephonyManager phone = (TelephonyManager)
2013                mContext.getSystemService(Context.TELEPHONY_SERVICE);
2014        final int phoneType = phone.getPhoneType();
2015        if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
2016            GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
2017            if ((gsm_cell != null) && (phone.getNetworkOperator() != null)
2018                    && (phone.getNetworkOperator().length() > 3)) {
2019                int type;
2020                int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
2021                int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
2022                int networkType = phone.getNetworkType();
2023                if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
2024                    || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
2025                    || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
2026                    || networkType == TelephonyManager.NETWORK_TYPE_HSPA
2027                    || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
2028                    type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
2029                } else {
2030                    type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
2031                }
2032                native_agps_set_ref_location_cellid(type, mcc, mnc,
2033                        gsm_cell.getLac(), gsm_cell.getCid());
2034            } else {
2035                Log.e(TAG,"Error getting cell location info.");
2036            }
2037        } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
2038            Log.e(TAG, "CDMA not supported.");
2039        }
2040    }
2041
2042    private void sendMessage(int message, int arg, Object obj) {
2043        // hold a wake lock until this message is delivered
2044        // note that this assumes the message will not be removed from the queue before
2045        // it is handled (otherwise the wake lock would be leaked).
2046        mWakeLock.acquire();
2047        Log.i(TAG, "WakeLock acquired by sendMessage(" + message + ", " + arg + ", " + obj + ")");
2048        mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
2049    }
2050
2051    private final class ProviderHandler extends Handler {
2052        public ProviderHandler(Looper looper) {
2053            super(looper, null, true /*async*/);
2054        }
2055
2056        @Override
2057        public void handleMessage(Message msg) {
2058            int message = msg.what;
2059            switch (message) {
2060                case ENABLE:
2061                    if (msg.arg1 == 1) {
2062                        handleEnable();
2063                    } else {
2064                        handleDisable();
2065                    }
2066                    break;
2067                case SET_REQUEST:
2068                    GpsRequest gpsRequest = (GpsRequest) msg.obj;
2069                    handleSetRequest(gpsRequest.request, gpsRequest.source);
2070                    break;
2071                case UPDATE_NETWORK_STATE:
2072                    handleUpdateNetworkState((Network) msg.obj);
2073                    break;
2074                case REQUEST_SUPL_CONNECTION:
2075                    handleRequestSuplConnection((InetAddress) msg.obj);
2076                    break;
2077                case RELEASE_SUPL_CONNECTION:
2078                    handleReleaseSuplConnection(msg.arg1);
2079                    break;
2080                case INJECT_NTP_TIME:
2081                    handleInjectNtpTime();
2082                    break;
2083                case DOWNLOAD_XTRA_DATA:
2084                    if (mSupportsXtra) {
2085                        handleDownloadXtraData();
2086                    }
2087                    break;
2088                case INJECT_NTP_TIME_FINISHED:
2089                    mInjectNtpTimePending = STATE_IDLE;
2090                    break;
2091                case DOWNLOAD_XTRA_DATA_FINISHED:
2092                    mDownloadXtraDataPending = STATE_IDLE;
2093                    break;
2094                case UPDATE_LOCATION:
2095                    handleUpdateLocation((Location) msg.obj);
2096                    break;
2097                case SUBSCRIPTION_OR_SIM_CHANGED:
2098                    subscriptionOrSimChanged(mContext);
2099                    break;
2100                case INITIALIZE_HANDLER:
2101                    handleInitialize();
2102                    break;
2103            }
2104            if (msg.arg2 == 1) {
2105                // wakelock was taken for this message, release it
2106                mWakeLock.release();
2107                Log.i(TAG, "WakeLock released by handleMessage(" + message + ", " + msg.arg1 + ", "
2108                        + msg.obj + ")");
2109            }
2110        }
2111
2112        /**
2113         * This method is bound to {@link #GnssLocationProvider(Context, ILocationManager, Looper)}.
2114         * It is in charge of loading properties and registering for events that will be posted to
2115         * this handler.
2116         */
2117        private void handleInitialize() {
2118            // load default GPS configuration
2119            // (this configuration might change in the future based on SIM changes)
2120            reloadGpsProperties(mContext, mProperties);
2121
2122            // TODO: When this object "finishes" we should unregister by invoking
2123            // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
2124            // This is not strictly necessary because it will be unregistered if the
2125            // notification fails but it is good form.
2126
2127            // Register for SubscriptionInfo list changes which is guaranteed
2128            // to invoke onSubscriptionsChanged the first time.
2129            SubscriptionManager.from(mContext)
2130                    .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
2131
2132            // listen for events
2133            IntentFilter intentFilter;
2134            if (native_is_agps_ril_supported()) {
2135                intentFilter = new IntentFilter();
2136                intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
2137                intentFilter.addDataScheme("sms");
2138                intentFilter.addDataAuthority("localhost", "7275");
2139                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
2140
2141                intentFilter = new IntentFilter();
2142                intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
2143                try {
2144                    intentFilter.addDataType("application/vnd.omaloc-supl-init");
2145                } catch (IntentFilter.MalformedMimeTypeException e) {
2146                    Log.w(TAG, "Malformed SUPL init mime type");
2147                }
2148                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
2149            } else if (DEBUG) {
2150                Log.d(TAG, "Skipped registration for SMS/WAP-PUSH messages because AGPS Ril in GPS"
2151                        + " HAL is not supported");
2152            }
2153
2154            intentFilter = new IntentFilter();
2155            intentFilter.addAction(ALARM_WAKEUP);
2156            intentFilter.addAction(ALARM_TIMEOUT);
2157            intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
2158            intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
2159            intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
2160            intentFilter.addAction(Intent.ACTION_SCREEN_ON);
2161            intentFilter.addAction(SIM_STATE_CHANGED);
2162            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
2163
2164            // register for connectivity change events, this is equivalent to the deprecated way of
2165            // registering for CONNECTIVITY_ACTION broadcasts
2166            NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
2167            networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
2168            networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
2169            NetworkRequest networkRequest = networkRequestBuilder.build();
2170            mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback);
2171
2172            // listen for PASSIVE_PROVIDER updates
2173            LocationManager locManager =
2174                    (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
2175            long minTime = 0;
2176            float minDistance = 0;
2177            boolean oneShot = false;
2178            LocationRequest request = LocationRequest.createFromDeprecatedProvider(
2179                    LocationManager.PASSIVE_PROVIDER,
2180                    minTime,
2181                    minDistance,
2182                    oneShot);
2183            // Don't keep track of this request since it's done on behalf of other clients
2184            // (which are kept track of separately).
2185            request.setHideFromAppOps(true);
2186            locManager.requestLocationUpdates(
2187                    request,
2188                    new NetworkLocationListener(),
2189                    getLooper());
2190        }
2191    }
2192
2193    private final class NetworkLocationListener implements LocationListener {
2194        @Override
2195        public void onLocationChanged(Location location) {
2196            // this callback happens on mHandler looper
2197            if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
2198                handleUpdateLocation(location);
2199            }
2200        }
2201        @Override
2202        public void onStatusChanged(String provider, int status, Bundle extras) { }
2203        @Override
2204        public void onProviderEnabled(String provider) { }
2205        @Override
2206        public void onProviderDisabled(String provider) { }
2207    }
2208
2209    private String getSelectedApn() {
2210        Uri uri = Uri.parse("content://telephony/carriers/preferapn");
2211        Cursor cursor = null;
2212        try {
2213            cursor = mContext.getContentResolver().query(
2214                    uri,
2215                    new String[] { "apn" },
2216                    null /* selection */,
2217                    null /* selectionArgs */,
2218                    Carriers.DEFAULT_SORT_ORDER);
2219            if (cursor != null && cursor.moveToFirst()) {
2220                return cursor.getString(0);
2221            } else {
2222                Log.e(TAG, "No APN found to select.");
2223            }
2224        } catch (Exception e) {
2225            Log.e(TAG, "Error encountered on selecting the APN.", e);
2226        } finally {
2227            if (cursor != null) {
2228                cursor.close();
2229            }
2230        }
2231
2232        return null;
2233    }
2234
2235    private int getApnIpType(String apn) {
2236        ensureInHandlerThread();
2237        if (apn == null) {
2238            return APN_INVALID;
2239        }
2240
2241        String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
2242        Cursor cursor = null;
2243        try {
2244            cursor = mContext.getContentResolver().query(
2245                    Carriers.CONTENT_URI,
2246                    new String[] { Carriers.PROTOCOL },
2247                    selection,
2248                    null,
2249                    Carriers.DEFAULT_SORT_ORDER);
2250
2251            if (null != cursor && cursor.moveToFirst()) {
2252                return translateToApnIpType(cursor.getString(0), apn);
2253            } else {
2254                Log.e(TAG, "No entry found in query for APN: " + apn);
2255            }
2256        } catch (Exception e) {
2257            Log.e(TAG, "Error encountered on APN query for: " + apn, e);
2258        } finally {
2259            if (cursor != null) {
2260                cursor.close();
2261            }
2262        }
2263
2264        return APN_INVALID;
2265    }
2266
2267    private int translateToApnIpType(String ipProtocol, String apn) {
2268        if ("IP".equals(ipProtocol)) {
2269            return APN_IPV4;
2270        }
2271        if ("IPV6".equals(ipProtocol)) {
2272            return APN_IPV6;
2273        }
2274        if ("IPV4V6".equals(ipProtocol)) {
2275            return APN_IPV4V6;
2276        }
2277
2278        // we hit the default case so the ipProtocol is not recognized
2279        String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn);
2280        Log.e(TAG, message);
2281        return APN_INVALID;
2282    }
2283
2284    private void setRouting() {
2285        if (mAGpsDataConnectionIpAddr == null) {
2286            return;
2287        }
2288
2289        // TODO: replace the use of this deprecated API
2290        boolean result = mConnMgr.requestRouteToHostAddress(
2291                ConnectivityManager.TYPE_MOBILE_SUPL,
2292                mAGpsDataConnectionIpAddr);
2293
2294        if (!result) {
2295            Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr);
2296        } else if (DEBUG) {
2297            Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr);
2298        }
2299    }
2300
2301    /**
2302     * @return {@code true} if there is a data network available for outgoing connections,
2303     *         {@code false} otherwise.
2304     */
2305    private boolean isDataNetworkConnected() {
2306        NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
2307        return activeNetworkInfo != null && activeNetworkInfo.isConnected();
2308    }
2309
2310    /**
2311     * Ensures the calling function is running in the thread associated with {@link #mHandler}.
2312     */
2313    private void ensureInHandlerThread() {
2314        if (mHandler != null && Looper.myLooper() == mHandler.getLooper()) {
2315            return;
2316        }
2317        throw new RuntimeException("This method must run on the Handler thread.");
2318    }
2319
2320    /**
2321     * @return A string representing the current state stored in {@link #mAGpsDataConnectionState}.
2322     */
2323    private String agpsDataConnStateAsString() {
2324        switch(mAGpsDataConnectionState) {
2325            case AGPS_DATA_CONNECTION_CLOSED:
2326                return "CLOSED";
2327            case AGPS_DATA_CONNECTION_OPEN:
2328                return "OPEN";
2329            case AGPS_DATA_CONNECTION_OPENING:
2330                return "OPENING";
2331            default:
2332                return "<Unknown>";
2333        }
2334    }
2335
2336    /**
2337     * @return A string representing the given GPS_AGPS_DATA status.
2338     */
2339    private String agpsDataConnStatusAsString(int agpsDataConnStatus) {
2340        switch (agpsDataConnStatus) {
2341            case GPS_AGPS_DATA_CONNECTED:
2342                return "CONNECTED";
2343            case GPS_AGPS_DATA_CONN_DONE:
2344                return "DONE";
2345            case GPS_AGPS_DATA_CONN_FAILED:
2346                return "FAILED";
2347            case GPS_RELEASE_AGPS_DATA_CONN:
2348                return "RELEASE";
2349            case GPS_REQUEST_AGPS_DATA_CONN:
2350                return "REQUEST";
2351            default:
2352                return "<Unknown>";
2353        }
2354    }
2355
2356    @Override
2357    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2358        StringBuilder s = new StringBuilder();
2359        s.append("  mFixInterval=").append(mFixInterval).append('\n');
2360        s.append("  mDisableGps (battery saver mode)=").append(mDisableGps).append('\n');
2361        s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities));
2362        s.append(" ( ");
2363        if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHEDULING ");
2364        if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
2365        if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
2366        if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
2367        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
2368        if (hasCapability(GPS_CAPABILITY_GEOFENCING)) s.append("GEOFENCING ");
2369        if (hasCapability(GPS_CAPABILITY_MEASUREMENTS)) s.append("MEASUREMENTS ");
2370        if (hasCapability(GPS_CAPABILITY_NAV_MESSAGES)) s.append("NAV_MESSAGES ");
2371        s.append(")\n");
2372
2373        s.append(native_get_internal_state());
2374        pw.append(s);
2375    }
2376
2377    /**
2378     * A simple implementation of exponential backoff.
2379     */
2380    private static final class BackOff {
2381        private static final int MULTIPLIER = 2;
2382        private final long mInitIntervalMillis;
2383        private final long mMaxIntervalMillis;
2384        private long mCurrentIntervalMillis;
2385
2386        public BackOff(long initIntervalMillis, long maxIntervalMillis) {
2387            mInitIntervalMillis = initIntervalMillis;
2388            mMaxIntervalMillis = maxIntervalMillis;
2389
2390            mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
2391        }
2392
2393        public long nextBackoffMillis() {
2394            if (mCurrentIntervalMillis > mMaxIntervalMillis) {
2395                return mMaxIntervalMillis;
2396            }
2397
2398            mCurrentIntervalMillis *= MULTIPLIER;
2399            return mCurrentIntervalMillis;
2400        }
2401
2402        public void reset() {
2403            mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
2404        }
2405    }
2406
2407    // for GPS SV statistics
2408    private static final int MAX_SVS = 64;
2409
2410    // preallocated arrays, to avoid memory allocation in reportStatus()
2411    private int mSvidWithFlags[] = new int[MAX_SVS];
2412    private float mCn0s[] = new float[MAX_SVS];
2413    private float mSvElevations[] = new float[MAX_SVS];
2414    private float mSvAzimuths[] = new float[MAX_SVS];
2415    private int mSvCount;
2416    // preallocated to avoid memory allocation in reportNmea()
2417    private byte[] mNmeaBuffer = new byte[120];
2418
2419    static { class_init_native(); }
2420    private static native void class_init_native();
2421    private static native boolean native_is_supported();
2422    private static native boolean native_is_agps_ril_supported();
2423    private static native boolean native_is_gnss_configuration_supported();
2424
2425    private native boolean native_init();
2426    private native void native_cleanup();
2427    private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
2428            int preferred_accuracy, int preferred_time);
2429    private native boolean native_start();
2430    private native boolean native_stop();
2431    private native void native_delete_aiding_data(int flags);
2432    // returns number of SVs
2433    // mask[0] is ephemeris mask and mask[1] is almanac mask
2434    private native int native_read_sv_status(int[] prnWithFlags, float[] cn0s, float[] elevations,
2435            float[] azimuths);
2436    private native int native_read_nmea(byte[] buffer, int bufferSize);
2437    private native void native_inject_location(double latitude, double longitude, float accuracy);
2438
2439    // XTRA Support
2440    private native void native_inject_time(long time, long timeReference, int uncertainty);
2441    private native boolean native_supports_xtra();
2442    private native void native_inject_xtra_data(byte[] data, int length);
2443
2444    // DEBUG Support
2445    private native String native_get_internal_state();
2446
2447    // AGPS Support
2448    private native void native_agps_data_conn_open(String apn, int apnIpType);
2449    private native void native_agps_data_conn_closed();
2450    private native void native_agps_data_conn_failed();
2451    private native void native_agps_ni_message(byte [] msg, int length);
2452    private native void native_set_agps_server(int type, String hostname, int port);
2453
2454    // Network-initiated (NI) Support
2455    private native void native_send_ni_response(int notificationId, int userResponse);
2456
2457    // AGPS ril suport
2458    private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
2459            int lac, int cid);
2460    private native void native_agps_set_id(int type, String setid);
2461
2462    private native void native_update_network_state(boolean connected, int type,
2463            boolean roaming, boolean available, String extraInfo, String defaultAPN);
2464
2465    // Hardware Geofence support.
2466    private static native boolean native_is_geofence_supported();
2467    private static native boolean native_add_geofence(int geofenceId, double latitude,
2468            double longitude, double radius, int lastTransition,int monitorTransitions,
2469            int notificationResponsivenes, int unknownTimer);
2470    private static native boolean native_remove_geofence(int geofenceId);
2471    private static native boolean native_resume_geofence(int geofenceId, int transitions);
2472    private static native boolean native_pause_geofence(int geofenceId);
2473
2474    // Gps Hal measurements support.
2475    private static native boolean native_is_measurement_supported();
2476    private native boolean native_start_measurement_collection();
2477    private native boolean native_stop_measurement_collection();
2478
2479    // Gps Navigation message support.
2480    private static native boolean native_is_navigation_message_supported();
2481    private native boolean native_start_navigation_message_collection();
2482    private native boolean native_stop_navigation_message_collection();
2483
2484    // GNSS Configuration
2485    private static native void native_configuration_update(String configData);
2486}
2487