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