OSDN Git Service

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