OSDN Git Service

DO NOT MERGE - SUPL ES Extension - Safer Init and Not After Boot
[android-x86/frameworks-base.git] / services / core / java / com / android / server / location / GnssLocationProvider.java
index 17c1000..8c1bec3 100644 (file)
 
 package com.android.server.location;
 
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.GpsNetInitiatedHandler;
-import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
@@ -56,9 +49,11 @@ import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.PowerSaveState;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -72,33 +67,46 @@ import android.os.WorkSource;
 import android.provider.Settings;
 import android.provider.Telephony.Carriers;
 import android.provider.Telephony.Sms.Intents;
-import android.telephony.SmsMessage;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.NtpTrustedTime;
 
-import java.io.ByteArrayOutputStream;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
+import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import com.android.server.power.BatterySaverPolicy;
+import com.android.server.power.BatterySaverPolicy.ServiceType;
+
+import libcore.io.IoUtils;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.io.StringReader;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.List;
 import java.util.Map.Entry;
 import java.util.Properties;
-
-import libcore.io.IoUtils;
+import java.util.Map;
+import java.util.HashMap;
 
 /**
- * A GPS implementation of LocationProvider used by LocationManager.
+ * A GNSS implementation of LocationProvider used by LocationManager.
  *
  * {@hide}
  */
@@ -113,23 +121,23 @@ public class GnssLocationProvider implements LocationProviderInterface {
             true, true, false, false, true, true, true,
             Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
 
-    // these need to match GpsPositionMode enum in gps.h
+    // these need to match GnssPositionMode enum in IGnss.hal
     private static final int GPS_POSITION_MODE_STANDALONE = 0;
     private static final int GPS_POSITION_MODE_MS_BASED = 1;
     private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
 
-    // these need to match GpsPositionRecurrence enum in gps.h
+    // these need to match GnssPositionRecurrence enum in IGnss.hal
     private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
     private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
 
-    // these need to match GpsStatusValue defines in gps.h
+    // these need to match GnssStatusValue enum in IGnssCallback.hal
     private static final int GPS_STATUS_NONE = 0;
     private static final int GPS_STATUS_SESSION_BEGIN = 1;
     private static final int GPS_STATUS_SESSION_END = 2;
     private static final int GPS_STATUS_ENGINE_ON = 3;
     private static final int GPS_STATUS_ENGINE_OFF = 4;
 
-    // these need to match GpsApgsStatusValue defines in gps.h
+    // these need to match AGnssStatusValue enum in IAGnssCallback.hal
     /** AGPS status event values. */
     private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
     private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
@@ -137,15 +145,19 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private static final int GPS_AGPS_DATA_CONN_DONE = 4;
     private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
 
-    // these need to match GpsLocationFlags enum in gps.h
+    // these need to match GnssLocationFlags enum in types.hal
     private static final int LOCATION_INVALID = 0;
     private static final int LOCATION_HAS_LAT_LONG = 1;
     private static final int LOCATION_HAS_ALTITUDE = 2;
     private static final int LOCATION_HAS_SPEED = 4;
     private static final int LOCATION_HAS_BEARING = 8;
-    private static final int LOCATION_HAS_ACCURACY = 16;
+    private static final int LOCATION_HAS_HORIZONTAL_ACCURACY = 16;
+    private static final int LOCATION_HAS_VERTICAL_ACCURACY = 32;
+    private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
+    private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
+
 
-    // IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
+    // IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
     private static final int GPS_DELETE_EPHEMERIS = 0x0001;
     private static final int GPS_DELETE_ALMANAC = 0x0002;
     private static final int GPS_DELETE_POSITION = 0x0004;
@@ -160,7 +172,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
     private static final int GPS_DELETE_ALL = 0xFFFF;
 
-    // The GPS_CAPABILITY_* flags must match the values in gps.h
+    // The GPS_CAPABILITY_* flags must match Capabilities enum in IGnssCallback.hal
     private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
     private static final int GPS_CAPABILITY_MSB = 0x0000002;
     private static final int GPS_CAPABILITY_MSA = 0x0000004;
@@ -174,11 +186,11 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
     private static final int AGPS_SUPL_MODE_MSB = 0x01;
 
-    // these need to match AGpsType enum in gps.h
+    // these need to match AGnssType enum in IAGnssCallback.hal
     private static final int AGPS_TYPE_SUPL = 1;
     private static final int AGPS_TYPE_C2K = 2;
 
-    // these must match the definitions in gps.h
+    // these must match the ApnIpType enum in IAGnss.hal
     private static final int APN_INVALID = 0;
     private static final int APN_IPV4 = 1;
     private static final int APN_IPV6 = 2;
@@ -210,28 +222,22 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
     private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
 
-    // Request ref location
-    private static final int AGPS_RIL_REQUEST_REFLOC_CELLID = 1;
-    private static final int AGPS_RIL_REQUEST_REFLOC_MAC = 2;
+    //TODO(b/33112647): Create gps_debug.conf with commented career parameters.
+    private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf";
 
     // ref. location info
     private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
     private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
-    private static final int AGPS_REG_LOCATION_TYPE_MAC        = 3;
 
     // set id info
     private static final int AGPS_SETID_TYPE_NONE = 0;
     private static final int AGPS_SETID_TYPE_IMSI = 1;
     private static final int AGPS_SETID_TYPE_MSISDN = 2;
 
-    private static final String PROPERTIES_FILE_PREFIX = "/etc/gps";
-    private static final String PROPERTIES_FILE_SUFFIX = ".conf";
-    private static final String DEFAULT_PROPERTIES_FILE = PROPERTIES_FILE_PREFIX + PROPERTIES_FILE_SUFFIX;
-
     private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
     private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
 
-    // GPS Geofence errors. Should match gps.h constants.
+    // GPS Geofence errors. Should match GeofenceStatus enum in IGnssGeofenceCallback.hal.
     private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
     private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
     private static final int GPS_GEOFENCE_ERROR_ID_EXISTS  = -101;
@@ -244,14 +250,6 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private static final int TCP_MIN_PORT = 0;
     private static final int TCP_MAX_PORT = 0xffff;
 
-    // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode.
-    private static final int BATTERY_SAVER_MODE_NO_CHANGE = 0;
-    // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
-    // is enabled and the screen is off.
-    private static final int BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF = 1;
-    // Secure setting for GPS behavior when battery saver mode is on.
-    private static final String BATTERY_SAVER_GPS_MODE = "batterySaverGpsMode";
-
     /** simpler wrapper for ProviderRequest + Worksource */
     private static class GpsRequest {
         public ProviderRequest request;
@@ -264,8 +262,6 @@ public class GnssLocationProvider implements LocationProviderInterface {
 
     private Object mLock = new Object();
 
-    private int mLocationFlags = LOCATION_INVALID;
-
     // current status
     private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
 
@@ -402,10 +398,6 @@ public class GnssLocationProvider implements LocationProviderInterface {
     // Persist property for LPP_PROFILE
     private final static String LPP_PROFILE = "persist.sys.gps.lpp";
 
-    // VZW PLMN info
-    private static final String[] VzwMccMncList = {"311480", "310004", "20404"};
-    // corresponding GID1 value, empty string means ignore gid1 match.
-    private static final String[] VzwGid1List = {"", "", "BAE0000000000000"};
 
 
     private final PowerManager mPowerManager;
@@ -420,9 +412,17 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private WorkSource mClientSource = new WorkSource();
 
     private GeofenceHardwareImpl mGeofenceHardwareImpl;
-
     private int mYearOfHardware = 0;
 
+    // Set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
+    // stops output right at 600m/s, depriving this of the information of a device that reaches
+    // greater than 600m/s, and higher than the speed of sound to avoid impacting most use cases.
+    private static final float ITAR_SPEED_LIMIT_METERS_PER_SECOND = 400.0F;
+    private boolean mItarSpeedLimitExceeded = false;
+
+    // GNSS Metrics
+    private GnssMetrics mGnssMetrics;
+
     private final IGnssStatusProvider mGnssStatusProvider = new IGnssStatusProvider.Stub() {
         @Override
         public void registerGnssStatusCallback(IGnssStatusListener callback) {
@@ -464,6 +464,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
             if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
                 xtraDownloadRequest();
             }
+            // Always on, notify HAL so it can get data it needs
             sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
         }
 
@@ -481,14 +482,14 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private final ConnectivityManager.NetworkCallback mSuplConnectivityCallback =
             new ConnectivityManager.NetworkCallback() {
         @Override
-        public void onLost(Network network) {
-            releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
+        public void onAvailable(Network network) {
+            // Specific to a change to a SUPL enabled network becoming ready
+            sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
         }
 
         @Override
-        public void onUnavailable() {
-            // timeout, it was not possible to establish the required connection
-            releaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
+        public void onLost(Network network) {
+            releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
         }
     };
 
@@ -504,10 +505,6 @@ public class GnssLocationProvider implements LocationProviderInterface {
                 startNavigating(false);
             } else if (action.equals(ALARM_TIMEOUT)) {
                 hibernate();
-            } else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
-                checkSmsSuplInit(intent);
-            } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
-                checkWapSuplInit(intent);
             } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
                     || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
                     || Intent.ACTION_SCREEN_OFF.equals(action)
@@ -527,42 +524,32 @@ public class GnssLocationProvider implements LocationProviderInterface {
         }
     };
 
-    private final boolean isVerizon(String mccMnc, String imsi, String groupId) {
-        if (DEBUG) Log.d(TAG, "simOperator: " + mccMnc);
-        if (!TextUtils.isEmpty(mccMnc) || !TextUtils.isEmpty(imsi)) {
-            for (int i = 0; i < VzwMccMncList.length; i++) {
-                if ((!TextUtils.isEmpty(mccMnc) && mccMnc.equals(VzwMccMncList[i])) ||
-                        (!TextUtils.isEmpty(imsi) && imsi.startsWith(VzwMccMncList[i]))) {
-                    // check gid too if needed
-                    if (TextUtils.isEmpty(VzwGid1List[i]) || VzwGid1List[i].equals(groupId)) {
-                        if (DEBUG) Log.d(TAG, "Verizon UICC");
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
     private void subscriptionOrSimChanged(Context context) {
         if (DEBUG) Log.d(TAG, "received SIM related action: ");
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         String mccMnc = phone.getSimOperator();
-        String imsi = phone.getSubscriberId();
-        String groupId = phone.getGroupIdLevel1();
+        boolean isKeepLppProfile = false;
         if (!TextUtils.isEmpty(mccMnc)) {
             if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
             synchronized (mLock) {
-                if (isVerizon(mccMnc, imsi, groupId)) {
-                        // load current properties for carrier VZW
-                        loadPropertiesFromResource(context, mProperties);
-                        String lpp_profile = mProperties.getProperty("LPP_PROFILE");
-                        // set the persist property LPP_PROFILE for VZW
+                if (configManager != null) {
+                    PersistableBundle b = configManager.getConfig();
+                    isKeepLppProfile = b.getBoolean(CarrierConfigManager.KEY_PERSIST_LPP_MODE_BOOL);
+                }
+                if (isKeepLppProfile) {
+                    // load current properties for the carrier
+                    loadPropertiesFromResource(context, mProperties);
+                    String lpp_profile = mProperties.getProperty("LPP_PROFILE");
+                    // set the persist property LPP_PROFILE for the value
+                    if (lpp_profile != null) {
                         SystemProperties.set(LPP_PROFILE, lpp_profile);
+                    }
                 } else {
-                        // reset the persist property for Non VZW
-                        SystemProperties.set(LPP_PROFILE, "");
+                    // reset the persist property
+                    SystemProperties.set(LPP_PROFILE, "");
                 }
                 reloadGpsProperties(context, mProperties);
                 mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
@@ -572,39 +559,15 @@ public class GnssLocationProvider implements LocationProviderInterface {
         }
     }
 
-    private void checkSmsSuplInit(Intent intent) {
-        SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
-        if (messages == null) {
-            Log.e(TAG, "Message does not exist in the intent.");
-            return;
-        }
-
-        for (SmsMessage message : messages) {
-            if (message != null && message.mWrappedSmsMessage != null) {
-                byte[] suplInit = message.getUserData();
-                if (suplInit != null) {
-                    native_agps_ni_message(suplInit, suplInit.length);
-                }
-            }
-        }
-    }
-
-    private void checkWapSuplInit(Intent intent) {
-        byte[] suplInit = intent.getByteArrayExtra("data");
-        if (suplInit == null) {
-            return;
-        }
-        native_agps_ni_message(suplInit,suplInit.length);
-    }
-
     private void updateLowPowerMode() {
         // Disable GPS if we are in device idle mode.
         boolean disableGps = mPowerManager.isDeviceIdleMode();
-        switch (Settings.Secure.getInt(mContext.getContentResolver(), BATTERY_SAVER_GPS_MODE,
-                BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF)) {
-            case BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF:
+        final PowerSaveState result =
+                mPowerManager.getPowerSaveState(ServiceType.GPS);
+        switch (result.gpsMode) {
+            case BatterySaverPolicy.GPS_MODE_DISABLED_WHEN_SCREEN_OFF:
                 // If we are in battery saver mode and the screen is off, disable GPS.
-                disableGps |= mPowerManager.isPowerSaveMode() && !mPowerManager.isInteractive();
+                disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive();
                 break;
         }
         if (disableGps != mDisableGps) {
@@ -617,28 +580,23 @@ public class GnssLocationProvider implements LocationProviderInterface {
         return native_is_supported();
     }
 
+    interface SetCarrierProperty {
+        public boolean set(int value);
+    }
+
     private void reloadGpsProperties(Context context, Properties properties) {
         if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
         loadPropertiesFromResource(context, properties);
 
-        boolean isPropertiesLoadedFromFile = false;
-        final String gpsHardware = SystemProperties.get("ro.hardware.gps");
-
-        if (!TextUtils.isEmpty(gpsHardware)) {
-            final String propFilename =
-                    PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX;
-            isPropertiesLoadedFromFile =
-                    loadPropertiesFromFile(propFilename, properties);
-        }
-        if (!isPropertiesLoadedFromFile) {
-            loadPropertiesFromFile(DEFAULT_PROPERTIES_FILE, properties);
-        }
-        if (DEBUG) Log.d(TAG, "GPS properties reloaded, size = " + properties.size());
         String lpp_prof = SystemProperties.get(LPP_PROFILE);
         if (!TextUtils.isEmpty(lpp_prof)) {
                 // override default value of this if lpp_prof is not empty
                 properties.setProperty("LPP_PROFILE", lpp_prof);
         }
+        /*
+         * Overlay carrier properties from a debug configuration file.
+         */
+        loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties);
         // TODO: we should get rid of C2K specific setting.
         setSuplHostPort(properties.getProperty("SUPL_HOST"),
                         properties.getProperty("SUPL_PORT"));
@@ -651,16 +609,33 @@ public class GnssLocationProvider implements LocationProviderInterface {
                 Log.e(TAG, "unable to parse C2K_PORT: " + portString);
             }
         }
-
         if (native_is_gnss_configuration_supported()) {
-            try {
-                // Convert properties to string contents and send it to HAL.
-                ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
-                properties.store(baos, null);
-                native_configuration_update(baos.toString());
-                if (DEBUG) Log.d(TAG, "final config = " + baos.toString());
-            } catch (IOException ex) {
-                Log.e(TAG, "failed to dump properties contents");
+            Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
+                {
+                    put("SUPL_VER", (val) -> native_set_supl_version(val));
+                    put("SUPL_MODE", (val) -> native_set_supl_mode(val));
+                    put("SUPL_ES", (val) -> native_set_supl_es(val));
+                    put("LPP_PROFILE", (val) -> native_set_lpp_profile(val));
+                    put("A_GLONASS_POS_PROTOCOL_SELECT", (val) -> native_set_gnss_pos_protocol_select(val));
+                    put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL", (val) -> native_set_emergency_supl_pdn(val));
+                    put("GPS_LOCK", (val) -> native_set_gps_lock(val));
+                }
+            };
+
+            for(Entry<String, SetCarrierProperty> entry : map.entrySet()) {
+                String propertyName = entry.getKey();
+                String propertyValueString = properties.getProperty(propertyName);
+                if (propertyValueString != null) {
+                    try {
+                          int propertyValueInt = Integer.decode(propertyValueString);
+                          boolean result = entry.getValue().set(propertyValueInt);
+                          if (result == false) {
+                              Log.e(TAG, "Unable to set " + propertyName);
+                          }
+                    } catch (NumberFormatException e) {
+                          Log.e(TAG, "unable to parse propertyName: " + propertyValueString);
+                    }
+                }
             }
         } else if (DEBUG) {
             Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
@@ -707,7 +682,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
             }
 
         } catch (IOException e) {
-            Log.w(TAG, "Could not open GPS configuration file " + filename);
+            if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filename);
             return false;
         }
         return true;
@@ -754,12 +729,11 @@ public class GnssLocationProvider implements LocationProviderInterface {
         // while IO initialization and registration is delegated to our internal handler
         // this approach is just fine because events are posted to our handler anyway
         mProperties = new Properties();
-        sendMessage(INITIALIZE_HANDLER, 0, null);
-
-        // Create a GPS net-initiated handler.
+        // Create a GPS net-initiated handler (also needed by handleInitialize)
         mNIHandler = new GpsNetInitiatedHandler(context,
                                                 mNetInitiatedListener,
                                                 mSuplEsEnabled);
+        sendMessage(INITIALIZE_HANDLER, 0, null);
 
         mListenerHelper = new GnssStatusListenerHelper(mHandler) {
             @Override
@@ -816,6 +790,19 @@ public class GnssLocationProvider implements LocationProviderInterface {
                 return isEnabled();
             }
         };
+        mGnssMetrics = new GnssMetrics();
+
+        /*
+        * A cycle of native_init() and native_cleanup() is needed so that callbacks are registered
+        * after bootup even when location is disabled. This will allow Emergency SUPL to work even
+        * when location is disabled before device restart.
+        * */
+        boolean isInitialized = native_init();
+        if(!isInitialized) {
+            Log.d(TAG, "Failed to initialize at bootup");
+        } else {
+            native_cleanup();
+        }
     }
 
     /**
@@ -921,8 +908,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
         NetworkRequest request = requestBuilder.build();
         mConnMgr.requestNetwork(
                 request,
-                mSuplConnectivityCallback,
-                ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS);
+                mSuplConnectivityCallback);
     }
 
     private void handleReleaseSuplConnection(int agpsDataConnStatus) {
@@ -983,9 +969,9 @@ public class GnssLocationProvider implements LocationProviderInterface {
                     long time = mNtpTime.getCachedNtpTime();
                     long timeReference = mNtpTime.getCachedNtpTimeReference();
                     long certainty = mNtpTime.getCacheCertainty();
-                    long now = System.currentTimeMillis();
 
                     if (DEBUG) {
+                        long now = System.currentTimeMillis();
                         Log.d(TAG, "NTP server returned: "
                                 + time + " (" + new Date(time)
                                 + ") reference: " + timeReference
@@ -1182,6 +1168,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
 
             mGnssMeasurementsProvider.onGpsEnabledChanged();
             mGnssNavigationMessageProvider.onGpsEnabledChanged();
+            enableBatching();
         } else {
             synchronized (mLock) {
                 mEnabled = false;
@@ -1213,6 +1200,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
         mAlarmManager.cancel(mWakeupIntent);
         mAlarmManager.cancel(mTimeoutIntent);
 
+        disableBatching();
         // do this before releasing wakelock
         native_cleanup();
 
@@ -1229,21 +1217,29 @@ public class GnssLocationProvider implements LocationProviderInterface {
 
     @Override
     public int getStatus(Bundle extras) {
-        if (extras != null) {
-            extras.putInt("satellites", mSvCount);
-        }
+        setLocationExtras(extras);
         return mStatus;
     }
 
-    private void updateStatus(int status, int svCount) {
-        if (status != mStatus || svCount != mSvCount) {
+    private void updateStatus(int status, int svCount, int meanCn0, int maxCn0) {
+        if (status != mStatus || svCount != mSvCount || meanCn0 != mMeanCn0 || maxCn0 != mMaxCn0 ) {
             mStatus = status;
             mSvCount = svCount;
-            mLocationExtras.putInt("satellites", svCount);
+            mMeanCn0 = meanCn0;
+            mMaxCn0 = maxCn0;
+            setLocationExtras(mLocationExtras);
             mStatusUpdateTime = SystemClock.elapsedRealtime();
         }
     }
 
+    private void setLocationExtras(Bundle extras) {
+        if (extras != null) {
+            extras.putInt("satellites", mSvCount);
+            extras.putInt("meanCn0", mMeanCn0);
+            extras.putInt("maxCn0", mMaxCn0);
+        }
+    }
+
     @Override
     public long getStatusUpdateTime() {
         return mStatusUpdateTime;
@@ -1448,6 +1444,12 @@ public class GnssLocationProvider implements LocationProviderInterface {
             mStarted = true;
             mSingleShot = singleShot;
             mPositionMode = GPS_POSITION_MODE_STANDALONE;
+            // Notify about suppressed output, if speed limit was previously exceeded.
+            // Elsewhere, we check again with every speed output reported.
+            if (mItarSpeedLimitExceeded) {
+                Log.i(TAG, "startNavigating with ITAR limit in place. Output limited  " +
+                        "until slow enough speed reported.");
+            }
 
             boolean agpsEnabled =
                     (Settings.Global.getInt(mContext.getContentResolver(),
@@ -1488,8 +1490,8 @@ public class GnssLocationProvider implements LocationProviderInterface {
             }
 
             // reset SV count to zero
-            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
-            mFixRequestTime = System.currentTimeMillis();
+            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0, 0, 0);
+            mFixRequestTime = SystemClock.elapsedRealtime();
             if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
                 // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
                 // and our fix interval is not short
@@ -1509,10 +1511,9 @@ public class GnssLocationProvider implements LocationProviderInterface {
             native_stop();
             mTimeToFirstFix = 0;
             mLastFixTime = 0;
-            mLocationFlags = LOCATION_INVALID;
 
             // reset SV count to zero
-            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
+            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0, 0, 0);
         }
     }
 
@@ -1533,41 +1534,25 @@ public class GnssLocationProvider implements LocationProviderInterface {
     /**
      * called from native code to update our position.
      */
-    private void reportLocation(int flags, double latitude, double longitude, double altitude,
-            float speed, float bearing, float accuracy, long timestamp) {
-        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
-                " timestamp: " + timestamp);
+    private void reportLocation(boolean hasLatLong, Location location) {
+        if (location.hasSpeed()) {
+            mItarSpeedLimitExceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
+        }
+
+        if (mItarSpeedLimitExceeded) {
+            Log.i(TAG, "Hal reported a speed in excess of ITAR limit." +
+                    "  GPS/GNSS Navigation output blocked.");
+            mGnssMetrics.logReceivedLocationStatus(false);
+            return;  // No output of location allowed
+        }
+
+        if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
 
         synchronized (mLocation) {
-            mLocationFlags = flags;
-            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
-                mLocation.setLatitude(latitude);
-                mLocation.setLongitude(longitude);
-                mLocation.setTime(timestamp);
-                // It would be nice to push the elapsed real-time timestamp
-                // further down the stack, but this is still useful
-                mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
-            }
-            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
-                mLocation.setAltitude(altitude);
-            } else {
-                mLocation.removeAltitude();
-            }
-            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
-                mLocation.setSpeed(speed);
-            } else {
-                mLocation.removeSpeed();
-            }
-            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
-                mLocation.setBearing(bearing);
-            } else {
-                mLocation.removeBearing();
-            }
-            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
-                mLocation.setAccuracy(accuracy);
-            } else {
-                mLocation.removeAccuracy();
-            }
+            mLocation = location;
+            // It would be nice to push the elapsed real-time timestamp
+            // further down the stack, but this is still useful
+            mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
             mLocation.setExtras(mLocationExtras);
 
             try {
@@ -1577,11 +1562,23 @@ public class GnssLocationProvider implements LocationProviderInterface {
             }
         }
 
-        mLastFixTime = System.currentTimeMillis();
+        mGnssMetrics.logReceivedLocationStatus(hasLatLong);
+        if (hasLatLong) {
+            if (location.hasAccuracy()) {
+                mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
+            }
+            if (mTimeToFirstFix > 0) {
+                int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
+                mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
+            }
+        }
+
+        mLastFixTime = SystemClock.elapsedRealtime();
         // report time to first fix
-        if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+        if (mTimeToFirstFix == 0 && hasLatLong) {
             mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
             if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
+            mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
 
             // notify status listeners
             mListenerHelper.onFirstFix(mTimeToFirstFix);
@@ -1602,7 +1599,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-            updateStatus(LocationProvider.AVAILABLE, mSvCount);
+            updateStatus(LocationProvider.AVAILABLE, mSvCount, mMeanCn0, mMaxCn0);
         }
 
        if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
@@ -1650,46 +1647,66 @@ public class GnssLocationProvider implements LocationProviderInterface {
      * called from native code to update SV info
      */
     private void reportSvStatus() {
-        int svCount = native_read_sv_status(mSvidWithFlags, mCn0s, mSvElevations, mSvAzimuths);
+        int svCount = native_read_sv_status(mSvidWithFlags,
+            mCn0s,
+            mSvElevations,
+            mSvAzimuths,
+            mSvCarrierFreqs);
         mListenerHelper.onSvStatusChanged(
                 svCount,
                 mSvidWithFlags,
                 mCn0s,
                 mSvElevations,
-                mSvAzimuths);
+                mSvAzimuths,
+                mSvCarrierFreqs);
+
+        // Log CN0 as part of GNSS metrics
+        mGnssMetrics.logCn0(mCn0s, svCount);
 
         if (VERBOSE) {
             Log.v(TAG, "SV count: " + svCount);
         }
-        // Calculate number of sets used in fix.
+        // Calculate number of satellites used in fix.
         int usedInFixCount = 0;
+        int maxCn0 = 0;
+        int meanCn0 = 0;
         for (int i = 0; i < svCount; i++) {
             if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
                 ++usedInFixCount;
+                if (mCn0s[i] > maxCn0) {
+                    maxCn0 = (int)mCn0s[i];
+                }
+                meanCn0 += mCn0s[i];
             }
             if (VERBOSE) {
                 Log.v(TAG, "svid: " + (mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
-                        " cn0: " + mCn0s[i]/10 +
+                        " cn0: " + mCn0s[i] +
                         " elev: " + mSvElevations[i] +
                         " azimuth: " + mSvAzimuths[i] +
+                        " carrier frequency: " + mSvCarrierFreqs[i] +
                         ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
                                 ? "  " : " E") +
                         ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
                                 ? "  " : " A") +
                         ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
-                                ? "" : "U"));
+                                ? "" : "U") +
+                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) == 0
+                        ? "" : "F"));
             }
         }
-        // return number of sets used in fix instead of total
-        updateStatus(mStatus, usedInFixCount);
+        if (usedInFixCount > 0) {
+            meanCn0 /= usedInFixCount;
+        }
+        // return number of sats used in fix instead of total reported
+        updateStatus(mStatus, usedInFixCount, meanCn0, maxCn0);
 
         if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
-            System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
+            SystemClock.elapsedRealtime() - mLastFixTime > RECENT_FIX_TIMEOUT) {
             // send an intent to notify that the GPS is no longer receiving fixes.
             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
+            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount, mMeanCn0, mMaxCn0);
         }
     }
 
@@ -1738,23 +1755,41 @@ public class GnssLocationProvider implements LocationProviderInterface {
      * called from native code to report NMEA data received
      */
     private void reportNmea(long timestamp) {
-        int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
-        String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
-        mListenerHelper.onNmeaReceived(timestamp, nmea);
+        if (!mItarSpeedLimitExceeded) {
+            int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
+            String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
+            mListenerHelper.onNmeaReceived(timestamp, nmea);
+        }
     }
 
     /**
-     * called from native code - Gps measurements callback
+     * called from native code - GNSS measurements callback
      */
     private void reportMeasurementData(GnssMeasurementsEvent event) {
-        mGnssMeasurementsProvider.onMeasurementsAvailable(event);
+        if (!mItarSpeedLimitExceeded) {
+            // send to handler to allow native to return quickly
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mGnssMeasurementsProvider.onMeasurementsAvailable(event);
+                }
+            });
+        }
     }
 
     /**
-     * called from native code - GPS navigation message callback
+     * called from native code - GNSS navigation message callback
      */
     private void reportNavigationMessage(GnssNavigationMessage event) {
-        mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
+        if (!mItarSpeedLimitExceeded) {
+            // send to handler to allow native to return quickly
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
+                }
+            });
+        }
     }
 
     /**
@@ -1801,46 +1836,109 @@ public class GnssLocationProvider implements LocationProviderInterface {
         };
     }
 
+    public interface GnssBatchingProvider {
+        /**
+         * Returns the GNSS batching size
+         */
+        int getSize();
+        /**
+         * Starts the hardware batching operation
+         */
+        boolean start(long periodNanos, boolean wakeOnFifoFull);
+        /**
+         * Forces a flush of existing locations from the hardware batching
+         */
+        void flush();
+        /**
+         * Stops the batching operation
+         */
+        boolean stop();
+    }
+
     /**
-     * called from native code to request XTRA data
+     * @hide
      */
-    private void xtraDownloadRequest() {
-        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
-        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
+    public GnssBatchingProvider getGnssBatchingProvider() {
+        return new GnssBatchingProvider() {
+            @Override
+            public int getSize() {
+                return native_get_batch_size();
+            }
+            @Override
+            public boolean start(long periodNanos, boolean wakeOnFifoFull) {
+                if (periodNanos <= 0) {
+                    Log.e(TAG, "Invalid periodNanos " + periodNanos +
+                            "in batching request, not started");
+                    return false;
+                }
+                return native_start_batch(periodNanos, wakeOnFifoFull);
+            }
+            @Override
+            public void flush() {
+                native_flush_batch();
+            }
+            @Override
+            public boolean stop() {
+                return native_stop_batch();
+            }
+        };
+    }
+
+    public interface GnssMetricsProvider {
+        /**
+         * Returns GNSS metrics as proto string
+         */
+        String getGnssMetricsAsProtoString();
     }
 
     /**
-     * Helper method to construct a location object.
+     * @hide
      */
-    private Location buildLocation(
-            int flags,
-            double latitude,
-            double longitude,
-            double altitude,
-            float speed,
-            float bearing,
-            float accuracy,
-            long timestamp) {
-        Location location = new Location(LocationManager.GPS_PROVIDER);
-        if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
-            location.setLatitude(latitude);
-            location.setLongitude(longitude);
-            location.setTime(timestamp);
-            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
-        }
-        if((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
-            location.setAltitude(altitude);
-        }
-        if((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
-            location.setSpeed(speed);
-        }
-        if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
-            location.setBearing(bearing);
-        }
-        if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
-            location.setAccuracy(accuracy);
+    public GnssMetricsProvider getGnssMetricsProvider() {
+        return new GnssMetricsProvider() {
+            @Override
+            public String getGnssMetricsAsProtoString() {
+                return mGnssMetrics.dumpGnssMetricsAsProtoString();
+            }
+        };
+    }
+
+    /**
+     * Initialize Batching if enabled
+     */
+    private void enableBatching() {
+        if (!native_init_batching()) {
+            Log.e(TAG, "Failed to initialize GNSS batching");
+        };
+    }
+
+    /**
+     * Disable batching
+     */
+    private void disableBatching() {
+        native_stop_batch();
+        native_cleanup_batching();
+    }
+
+    /**
+     * called from native code - GNSS location batch callback
+     */
+    private void reportLocationBatch(Location[] locationArray) {
+        List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
+        if(DEBUG) { Log.d(TAG, "Location batch of size " + locationArray.length + "reported"); }
+        try {
+            mILocationManager.reportLocationBatch(locations);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling reportLocationBatch");
         }
-        return location;
+    }
+
+    /**
+     * called from native code to request XTRA data
+     */
+    private void xtraDownloadRequest() {
+        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
+        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
     }
 
     /**
@@ -1869,21 +1967,12 @@ public class GnssLocationProvider implements LocationProviderInterface {
      * Called from native to report GPS Geofence transition
      * All geofence callbacks are called on the same thread
      */
-    private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
-            double longitude, double altitude, float speed, float bearing, float accuracy,
-            long timestamp, int transition, long transitionTimestamp) {
+    private void reportGeofenceTransition(int geofenceId, Location location, int transition,
+                                          long transitionTimestamp) {
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        Location location = buildLocation(
-                flags,
-                latitude,
-                longitude,
-                altitude,
-                speed,
-                bearing,
-                accuracy,
-                timestamp);
+
         mGeofenceHardwareImpl.reportGeofenceTransition(
                 geofenceId,
                 location,
@@ -1896,21 +1985,10 @@ public class GnssLocationProvider implements LocationProviderInterface {
     /**
      * called from native code to report GPS status change.
      */
-    private void reportGeofenceStatus(int status, int flags, double latitude,
-            double longitude, double altitude, float speed, float bearing, float accuracy,
-            long timestamp) {
+    private void reportGeofenceStatus(int status, Location location) {
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        Location location = buildLocation(
-                flags,
-                latitude,
-                longitude,
-                altitude,
-                speed,
-                bearing,
-                accuracy,
-                timestamp);
         int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
         if(status == GPS_GEOFENCE_AVAILABLE) {
             monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
@@ -1993,8 +2071,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
             String requestorId,
             String text,
             int requestorIdEncoding,
-            int textEncoding,
-            String extras  // Encoded extra data
+            int textEncoding
         )
     {
         Log.i(TAG, "reportNiNotification: entered");
@@ -2023,28 +2100,6 @@ public class GnssLocationProvider implements LocationProviderInterface {
         notification.requestorIdEncoding = requestorIdEncoding;
         notification.textEncoding = textEncoding;
 
-        // Process extras, assuming the format is
-        // one of more lines of "key = value"
-        Bundle bundle = new Bundle();
-
-        if (extras == null) extras = "";
-        Properties extraProp = new Properties();
-
-        try {
-            extraProp.load(new StringReader(extras));
-        }
-        catch (IOException e)
-        {
-            Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
-        }
-
-        for (Entry<Object, Object> ent : extraProp.entrySet())
-        {
-            bundle.putString((String) ent.getKey(), (String) ent.getValue());
-        }
-
-        notification.extras = bundle;
-
         mNIHandler.handleNiNotification(notification);
     }
 
@@ -2095,7 +2150,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
      * Called from native code to request reference location info
      */
 
-    private void requestRefLocation(int flags) {
+    private void requestRefLocation() {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
         final int phoneType = phone.getPhoneType();
@@ -2131,7 +2186,10 @@ public class GnssLocationProvider implements LocationProviderInterface {
         // note that this assumes the message will not be removed from the queue before
         // it is handled (otherwise the wake lock would be leaked).
         mWakeLock.acquire();
-        Log.i(TAG, "WakeLock acquired by sendMessage(" + message + ", " + arg + ", " + obj + ")");
+        if (Log.isLoggable(TAG, Log.INFO)) {
+            Log.i(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
+                    + ", " + obj + ")");
+        }
         mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
     }
 
@@ -2189,8 +2247,10 @@ public class GnssLocationProvider implements LocationProviderInterface {
             if (msg.arg2 == 1) {
                 // wakelock was taken for this message, release it
                 mWakeLock.release();
-                Log.i(TAG, "WakeLock released by handleMessage(" + message + ", " + msg.arg1 + ", "
-                        + msg.obj + ")");
+                if (Log.isLoggable(TAG, Log.INFO)) {
+                    Log.i(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
+                            + ", " + msg.arg1 + ", " + msg.obj + ")");
+                }
             }
         }
 
@@ -2444,9 +2504,46 @@ public class GnssLocationProvider implements LocationProviderInterface {
         }
     }
 
+    /**
+     * @return A string representing the given message ID.
+     */
+    private String messageIdAsString(int message) {
+        switch (message) {
+            case ENABLE:
+                return "ENABLE";
+            case SET_REQUEST:
+                return "SET_REQUEST";
+            case UPDATE_NETWORK_STATE:
+                return "UPDATE_NETWORK_STATE";
+            case REQUEST_SUPL_CONNECTION:
+                return "REQUEST_SUPL_CONNECTION";
+            case RELEASE_SUPL_CONNECTION:
+                return "RELEASE_SUPL_CONNECTION";
+            case INJECT_NTP_TIME:
+                return "INJECT_NTP_TIME";
+            case DOWNLOAD_XTRA_DATA:
+                return "DOWNLOAD_XTRA_DATA";
+            case INJECT_NTP_TIME_FINISHED:
+                return "INJECT_NTP_TIME_FINISHED";
+            case DOWNLOAD_XTRA_DATA_FINISHED:
+                return "DOWNLOAD_XTRA_DATA_FINISHED";
+            case UPDATE_LOCATION:
+                return "UPDATE_LOCATION";
+            case SUBSCRIPTION_OR_SIM_CHANGED:
+                return "SUBSCRIPTION_OR_SIM_CHANGED";
+            case INITIALIZE_HANDLER:
+                return "INITIALIZE_HANDLER";
+            default:
+                return "<Unknown>";
+        }
+    }
+
+
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         StringBuilder s = new StringBuilder();
+        s.append("  mStarted=").append(mStarted).append('\n');
         s.append("  mFixInterval=").append(mFixInterval).append('\n');
         s.append("  mDisableGps (battery saver mode)=").append(mDisableGps).append('\n');
         s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities));
@@ -2460,8 +2557,9 @@ public class GnssLocationProvider implements LocationProviderInterface {
         if (hasCapability(GPS_CAPABILITY_MEASUREMENTS)) s.append("MEASUREMENTS ");
         if (hasCapability(GPS_CAPABILITY_NAV_MESSAGES)) s.append("NAV_MESSAGES ");
         s.append(")\n");
-
-        s.append(native_get_internal_state());
+        s.append(mGnssMetrics.dumpGnssMetricsAsText());
+        s.append("  native internal state: ").append(native_get_internal_state());
+        s.append("\n");
         pw.append(s);
     }
 
@@ -2503,7 +2601,10 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private float mCn0s[] = new float[MAX_SVS];
     private float mSvElevations[] = new float[MAX_SVS];
     private float mSvAzimuths[] = new float[MAX_SVS];
+    private float mSvCarrierFreqs[] = new float[MAX_SVS];
     private int mSvCount;
+    private int mMeanCn0;
+    private int mMaxCn0;
     // preallocated to avoid memory allocation in reportNmea()
     private byte[] mNmeaBuffer = new byte[120];
 
@@ -2523,7 +2624,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
     // returns number of SVs
     // mask[0] is ephemeris mask and mask[1] is almanac mask
     private native int native_read_sv_status(int[] prnWithFlags, float[] cn0s, float[] elevations,
-            float[] azimuths);
+            float[] azimuths, float[] carrierFrequencies);
     private native int native_read_nmea(byte[] buffer, int bufferSize);
     private native void native_inject_location(double latitude, double longitude, float accuracy);
 
@@ -2573,5 +2674,20 @@ public class GnssLocationProvider implements LocationProviderInterface {
     private native boolean native_stop_navigation_message_collection();
 
     // GNSS Configuration
-    private static native void native_configuration_update(String configData);
+    private static native boolean native_set_supl_version(int version);
+    private static native boolean native_set_supl_mode(int mode);
+    private static native boolean native_set_supl_es(int es);
+    private static native boolean native_set_lpp_profile(int lppProfile);
+    private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
+    private static native boolean native_set_gps_lock(int gpsLock);
+    private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
+
+    // GNSS Batching
+    private static native int native_get_batch_size();
+    private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull);
+    private static native void native_flush_batch();
+    private static native boolean native_stop_batch();
+    private static native boolean native_init_batching();
+    private static native void native_cleanup_batching();
+
 }