OSDN Git Service

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