OSDN Git Service

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