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