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