OSDN Git Service

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