OSDN Git Service

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