OSDN Git Service

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