• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.gnss;
18 
19 import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
20 import static android.content.pm.PackageManager.FEATURE_WATCH;
21 import static android.location.provider.ProviderProperties.ACCURACY_FINE;
22 import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
23 
24 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
25 import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_GSM_CELLID;
26 import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_LTE_CELLID;
27 import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_NR_CELLID;
28 import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
29 import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_IMSI;
30 import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_MSISDN;
31 import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_NONE;
32 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_ALL;
33 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_ALMANAC;
34 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_CELLDB_INFO;
35 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_EPHEMERIS;
36 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_HEALTH;
37 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_IONO;
38 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_POSITION;
39 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_RTI;
40 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_SADATA;
41 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_SVDIR;
42 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_SVSTEER;
43 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_TIME;
44 import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_UTC;
45 import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_MODE_MS_ASSISTED;
46 import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_MODE_MS_BASED;
47 import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_MODE_STANDALONE;
48 import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_RECURRENCE_PERIODIC;
49 
50 import static java.lang.Math.abs;
51 import static java.lang.Math.max;
52 import static java.util.concurrent.TimeUnit.MILLISECONDS;
53 
54 import android.app.AlarmManager;
55 import android.app.AppOpsManager;
56 import android.content.BroadcastReceiver;
57 import android.content.ContentResolver;
58 import android.content.Context;
59 import android.content.Intent;
60 import android.content.IntentFilter;
61 import android.content.pm.PackageManager;
62 import android.database.ContentObserver;
63 import android.location.GnssCapabilities;
64 import android.location.GnssStatus;
65 import android.location.INetInitiatedListener;
66 import android.location.Location;
67 import android.location.LocationListener;
68 import android.location.LocationManager;
69 import android.location.LocationRequest;
70 import android.location.LocationResult;
71 import android.location.provider.ProviderProperties;
72 import android.location.provider.ProviderRequest;
73 import android.location.util.identity.CallerIdentity;
74 import android.os.BatteryStats;
75 import android.os.Bundle;
76 import android.os.Handler;
77 import android.os.PersistableBundle;
78 import android.os.PowerManager;
79 import android.os.RemoteException;
80 import android.os.ServiceManager;
81 import android.os.SystemClock;
82 import android.os.SystemProperties;
83 import android.os.UserHandle;
84 import android.os.WorkSource;
85 import android.os.WorkSource.WorkChain;
86 import android.provider.Settings;
87 import android.telephony.CarrierConfigManager;
88 import android.telephony.CellIdentity;
89 import android.telephony.CellIdentityGsm;
90 import android.telephony.CellIdentityLte;
91 import android.telephony.CellIdentityNr;
92 import android.telephony.CellIdentityWcdma;
93 import android.telephony.CellInfo;
94 import android.telephony.CellInfoGsm;
95 import android.telephony.CellInfoLte;
96 import android.telephony.CellInfoNr;
97 import android.telephony.CellInfoWcdma;
98 import android.telephony.SubscriptionManager;
99 import android.telephony.TelephonyManager;
100 import android.text.TextUtils;
101 import android.text.format.DateUtils;
102 import android.util.Log;
103 import android.util.TimeUtils;
104 
105 import com.android.internal.annotations.GuardedBy;
106 import com.android.internal.app.IBatteryStats;
107 import com.android.internal.location.GpsNetInitiatedHandler;
108 import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
109 import com.android.internal.util.FrameworkStatsLog;
110 import com.android.server.FgThread;
111 import com.android.server.location.gnss.GnssSatelliteBlocklistHelper.GnssSatelliteBlocklistCallback;
112 import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
113 import com.android.server.location.gnss.hal.GnssNative;
114 import com.android.server.location.injector.Injector;
115 import com.android.server.location.provider.AbstractLocationProvider;
116 
117 import java.io.FileDescriptor;
118 import java.io.PrintWriter;
119 import java.util.ArrayList;
120 import java.util.Arrays;
121 import java.util.Collections;
122 import java.util.Comparator;
123 import java.util.HashMap;
124 import java.util.HashSet;
125 import java.util.List;
126 import java.util.Objects;
127 import java.util.Set;
128 import java.util.concurrent.Executors;
129 import java.util.concurrent.TimeUnit;
130 
131 /**
132  * A GNSS implementation of LocationProvider used by LocationManager.
133  *
134  * {@hide}
135  */
136 public class GnssLocationProvider extends AbstractLocationProvider implements
137         InjectNtpTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
138         GnssNative.LocationCallbacks, GnssNative.SvStatusCallbacks, GnssNative.AGpsCallbacks,
139         GnssNative.PsdsCallbacks, GnssNative.NotificationCallbacks,
140         GnssNative.LocationRequestCallbacks, GnssNative.TimeCallbacks {
141 
142     private static final String TAG = "GnssLocationProvider";
143 
144     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
145     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
146 
147     private static final ProviderProperties PROPERTIES = new ProviderProperties.Builder()
148                 .setHasSatelliteRequirement(true)
149                 .setHasAltitudeSupport(true)
150                 .setHasSpeedSupport(true)
151                 .setHasBearingSupport(true)
152                 .setPowerUsage(POWER_USAGE_HIGH)
153                 .setAccuracy(ACCURACY_FINE)
154                 .build();
155 
156     // The AGPS SUPL mode
157     private static final int AGPS_SUPL_MODE_MSA = 0x02;
158     private static final int AGPS_SUPL_MODE_MSB = 0x01;
159 
160     // TCP/IP constants.
161     // Valid TCP/UDP port range is (0, 65535].
162     private static final int TCP_MIN_PORT = 0;
163     private static final int TCP_MAX_PORT = 0xffff;
164 
165     // 1 second, or 1 Hz frequency.
166     private static final long LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS = 1000;
167     // Default update duration in milliseconds for REQUEST_LOCATION.
168     private static final long LOCATION_UPDATE_DURATION_MILLIS = 10 * 1000;
169     // Update duration extension multiplier for emergency REQUEST_LOCATION.
170     private static final int EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER = 3;
171     // maximum length gnss batching may go for (1 day)
172     private static final int MIN_BATCH_INTERVAL_MS = (int) DateUtils.SECOND_IN_MILLIS;
173     private static final long MAX_BATCH_LENGTH_MS = DateUtils.DAY_IN_MILLIS;
174     private static final long MAX_BATCH_TIMESTAMP_DELTA_MS = 500;
175 
176     // Threadsafe class to hold stats reported in the Extras Bundle
177     private static class LocationExtras {
178         private int mSvCount;
179         private int mMeanCn0;
180         private int mMaxCn0;
181         private final Bundle mBundle;
182 
LocationExtras()183         LocationExtras() {
184             mBundle = new Bundle();
185         }
186 
set(int svCount, int meanCn0, int maxCn0)187         public void set(int svCount, int meanCn0, int maxCn0) {
188             synchronized (this) {
189                 mSvCount = svCount;
190                 mMeanCn0 = meanCn0;
191                 mMaxCn0 = maxCn0;
192             }
193             setBundle(mBundle);
194         }
195 
reset()196         public void reset() {
197             set(0, 0, 0);
198         }
199 
200         // Also used by outside methods to add to other bundles
setBundle(Bundle extras)201         public void setBundle(Bundle extras) {
202             if (extras != null) {
203                 synchronized (this) {
204                     extras.putInt("satellites", mSvCount);
205                     extras.putInt("meanCn0", mMeanCn0);
206                     extras.putInt("maxCn0", mMaxCn0);
207                 }
208             }
209         }
210 
getBundle()211         public Bundle getBundle() {
212             synchronized (this) {
213                 return new Bundle(mBundle);
214             }
215         }
216     }
217 
218     // stop trying if we do not receive a fix within 60 seconds
219     private static final int NO_FIX_TIMEOUT = 60 * 1000;
220 
221     // if the fix interval is below this we leave GPS on,
222     // if above then we cycle the GPS driver.
223     // Typical hot TTTF is ~5 seconds, so 10 seconds seems valid.
224     private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
225 
226     // how long to wait if we have a network error in NTP or PSDS downloading
227     // the initial value of the exponential backoff
228     // current setting - 5 minutes
229     private static final long RETRY_INTERVAL = 5 * 60 * 1000;
230     // how long to wait if we have a network error in NTP or PSDS downloading
231     // the max value of the exponential backoff
232     // current setting - 4 hours
233     private static final long MAX_RETRY_INTERVAL = 4 * 60 * 60 * 1000;
234 
235     // Timeout when holding wakelocks for downloading PSDS data.
236     private static final long DOWNLOAD_PSDS_DATA_TIMEOUT_MS = 60 * 1000;
237     private static final long WAKELOCK_TIMEOUT_MILLIS = 30 * 1000;
238 
239     // threshold for delay in GNSS engine turning off before warning & error
240     private static final long LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS = 2 * 1000;
241     private static final long LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS = 15 * 1000;
242 
243     private final Object mLock = new Object();
244 
245     private final Context mContext;
246     private final Handler mHandler;
247 
248     private final GnssNative mGnssNative;
249 
250     @GuardedBy("mLock")
251     private final ExponentialBackOff mPsdsBackOff = new ExponentialBackOff(RETRY_INTERVAL,
252             MAX_RETRY_INTERVAL);
253 
254     // True if we are enabled
255     @GuardedBy("mLock")
256     private boolean mGpsEnabled;
257 
258     @GuardedBy("mLock")
259     private boolean mBatchingEnabled;
260 
261     @GuardedBy("mLock")
262     private boolean mAutomotiveSuspend;
263 
264     private boolean mShutdown;
265     private boolean mStarted;
266     private boolean mBatchingStarted;
267     private AlarmManager.OnAlarmListener mBatchingAlarm;
268     private long mStartedChangedElapsedRealtime;
269     private int mFixInterval = 1000;
270 
271     private ProviderRequest mProviderRequest;
272 
273     private int mPositionMode;
274     private GnssPositionMode mLastPositionMode;
275 
276     // for calculating time to first fix
277     private long mFixRequestTime = 0;
278     // time to first fix for most recent session
279     private int mTimeToFirstFix = 0;
280     // time we received our last fix
281     private long mLastFixTime;
282 
283     private final WorkSource mClientSource = new WorkSource();
284 
285     // true if PSDS is supported
286     private boolean mSupportsPsds;
287     private final Object mPsdsPeriodicDownloadToken = new Object();
288     @GuardedBy("mLock")
289     private final PowerManager.WakeLock mDownloadPsdsWakeLock;
290     @GuardedBy("mLock")
291     private final Set<Integer> mPendingDownloadPsdsTypes = new HashSet<>();
292 
293     /**
294      * Properties loaded from PROPERTIES_FILE.
295      * It must be accessed only inside {@link #mHandler}.
296      */
297     private final GnssConfiguration mGnssConfiguration;
298 
299     private String mSuplServerHost;
300     private int mSuplServerPort = TCP_MIN_PORT;
301     private String mC2KServerHost;
302     private int mC2KServerPort;
303     private boolean mSuplEsEnabled = false;
304 
305     private final LocationExtras mLocationExtras = new LocationExtras();
306     private final NtpTimeHelper mNtpTimeHelper;
307     private final GnssSatelliteBlocklistHelper mGnssSatelliteBlocklistHelper;
308 
309     // Available only on GNSS HAL 2.0 implementations and later.
310     private GnssVisibilityControl mGnssVisibilityControl;
311 
312     private final GnssNetworkConnectivityHandler mNetworkConnectivityHandler;
313     private final GpsNetInitiatedHandler mNIHandler;
314 
315     // Wakelocks
316     private final PowerManager.WakeLock mWakeLock;
317 
318     private final AlarmManager mAlarmManager;
319     private final AlarmManager.OnAlarmListener mWakeupListener = this::startNavigating;
320     private final AlarmManager.OnAlarmListener mTimeoutListener = this::hibernate;
321 
322     private final AppOpsManager mAppOps;
323     private final IBatteryStats mBatteryStats;
324 
325     @GuardedBy("mLock")
326     private final ArrayList<Runnable> mFlushListeners = new ArrayList<>(0);
327 
328     // GNSS Metrics
329     private final GnssMetrics mGnssMetrics;
330 
331     /**
332      * Implements {@link GnssSatelliteBlocklistCallback#onUpdateSatelliteBlocklist}.
333      */
334     @Override
onUpdateSatelliteBlocklist(int[] constellations, int[] svids)335     public void onUpdateSatelliteBlocklist(int[] constellations, int[] svids) {
336         mHandler.post(() -> mGnssConfiguration.setSatelliteBlocklist(constellations, svids));
337         mGnssMetrics.resetConstellationTypes();
338     }
339 
subscriptionOrCarrierConfigChanged()340     private void subscriptionOrCarrierConfigChanged() {
341         if (DEBUG) Log.d(TAG, "received SIM related action: ");
342         TelephonyManager phone = (TelephonyManager)
343                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
344         CarrierConfigManager configManager = (CarrierConfigManager)
345                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
346         int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
347         if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
348             phone = phone.createForSubscriptionId(ddSubId);
349         }
350         String mccMnc = phone.getSimOperator();
351         boolean isKeepLppProfile = false;
352         if (!TextUtils.isEmpty(mccMnc)) {
353             if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
354             if (configManager != null) {
355                 PersistableBundle b = SubscriptionManager.isValidSubscriptionId(ddSubId)
356                         ? configManager.getConfigForSubId(ddSubId) : null;
357                 if (b != null) {
358                     isKeepLppProfile =
359                             b.getBoolean(CarrierConfigManager.Gps.KEY_PERSIST_LPP_MODE_BOOL);
360                 }
361             }
362             if (isKeepLppProfile) {
363                 // load current properties for the carrier of ddSubId
364                 mGnssConfiguration.loadPropertiesFromCarrierConfig(/* inEmergency= */ false,
365                         /* activeSubId= */ -1);
366                 String lpp_profile = mGnssConfiguration.getLppProfile();
367                 // set the persist property LPP_PROFILE for the value
368                 if (lpp_profile != null) {
369                     SystemProperties.set(GnssConfiguration.LPP_PROFILE, lpp_profile);
370                 }
371             } else {
372                 // reset the persist property
373                 SystemProperties.set(GnssConfiguration.LPP_PROFILE, "");
374             }
375             reloadGpsProperties();
376         } else {
377             if (DEBUG) Log.d(TAG, "SIM MCC/MNC is still not available");
378             // Reload gnss config for no SIM case
379             mGnssConfiguration.reloadGpsProperties();
380         }
381     }
382 
reloadGpsProperties()383     private void reloadGpsProperties() {
384         mGnssConfiguration.reloadGpsProperties();
385         setSuplHostPort();
386         // TODO: we should get rid of C2K specific setting.
387         mC2KServerHost = mGnssConfiguration.getC2KHost();
388         mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
389         mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
390         mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
391         mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
392         if (mGnssVisibilityControl != null) {
393             mGnssVisibilityControl.onConfigurationUpdated(mGnssConfiguration);
394         }
395     }
396 
GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative, GnssMetrics gnssMetrics)397     public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
398             GnssMetrics gnssMetrics) {
399         super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
400                 Collections.emptySet());
401 
402         mContext = context;
403         mGnssNative = gnssNative;
404         mGnssMetrics = gnssMetrics;
405 
406         // Create a wake lock
407         PowerManager powerManager = Objects.requireNonNull(
408                 mContext.getSystemService(PowerManager.class));
409         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*location*:" + TAG);
410         mWakeLock.setReferenceCounted(true);
411 
412         // Create a separate wake lock for psds downloader as it may be released due to timeout.
413         mDownloadPsdsWakeLock = powerManager.newWakeLock(
414                 PowerManager.PARTIAL_WAKE_LOCK, "*location*:PsdsDownload");
415         mDownloadPsdsWakeLock.setReferenceCounted(true);
416 
417         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
418 
419         // App ops service to keep track of who is accessing the GPS
420         mAppOps = mContext.getSystemService(AppOpsManager.class);
421 
422         // Battery statistics service to be notified when GPS turns on or off
423         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
424                 BatteryStats.SERVICE_NAME));
425 
426         // Construct internal handler
427         mHandler = FgThread.getHandler();
428 
429         // Load GPS configuration and register listeners in the background:
430         // some operations, such as opening files and registering broadcast receivers, can take a
431         // relative long time, so the ctor() is kept to create objects needed by this instance,
432         // while IO initialization and registration is delegated to our internal handler
433         // this approach is just fine because events are posted to our handler anyway
434         mGnssConfiguration = mGnssNative.getConfiguration();
435         // Create a GPS net-initiated handler (also needed by handleInitialize)
436         GpsNetInitiatedHandler.EmergencyCallCallback emergencyCallCallback =
437                 new GpsNetInitiatedHandler.EmergencyCallCallback() {
438 
439                     @Override
440                     public void onEmergencyCallStart(int subId) {
441                         if (!mGnssConfiguration.isActiveSimEmergencySuplEnabled()) {
442                             return;
443                         }
444                         mHandler.post(() -> mGnssConfiguration.reloadGpsProperties(
445                                 mNIHandler.getInEmergency(), subId));
446                     }
447 
448                     @Override
449                     public void onEmergencyCallEnd() {
450                         if (!mGnssConfiguration.isActiveSimEmergencySuplEnabled()) {
451                             return;
452                         }
453                         mHandler.postDelayed(() -> mGnssConfiguration.reloadGpsProperties(
454                                         /* inEmergency= */ false,
455                                         SubscriptionManager.getDefaultDataSubscriptionId()),
456                                 TimeUnit.SECONDS.toMillis(mGnssConfiguration.getEsExtensionSec()));
457                     }
458                 };
459         mNIHandler = new GpsNetInitiatedHandler(context,
460                 mNetInitiatedListener,
461                 emergencyCallCallback,
462                 mSuplEsEnabled);
463         // Trigger PSDS data download when the network comes up after booting.
464         mPendingDownloadPsdsTypes.add(GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX);
465         mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
466                 GnssLocationProvider.this::onNetworkAvailable,
467                 mHandler.getLooper(), mNIHandler);
468 
469         mNtpTimeHelper = new NtpTimeHelper(mContext, mHandler.getLooper(), this);
470         mGnssSatelliteBlocklistHelper =
471                 new GnssSatelliteBlocklistHelper(mContext,
472                         mHandler.getLooper(), this);
473 
474         setAllowed(true);
475 
476         mGnssNative.addBaseCallbacks(this);
477         mGnssNative.addLocationCallbacks(this);
478         mGnssNative.addSvStatusCallbacks(this);
479         mGnssNative.setAGpsCallbacks(this);
480         mGnssNative.setPsdsCallbacks(this);
481         mGnssNative.setNotificationCallbacks(this);
482         mGnssNative.setLocationRequestCallbacks(this);
483         mGnssNative.setTimeCallbacks(this);
484     }
485 
486     /** Called when system is ready. */
onSystemReady()487     public synchronized void onSystemReady() {
488         mContext.registerReceiverAsUser(new BroadcastReceiver() {
489             @Override
490             public void onReceive(Context context, Intent intent) {
491                 if (getSendingUserId() == UserHandle.USER_ALL) {
492                     mShutdown = true;
493                     updateEnabled();
494                 }
495             }
496         }, UserHandle.ALL, new IntentFilter(Intent.ACTION_SHUTDOWN), null, mHandler);
497 
498         mContext.getContentResolver().registerContentObserver(
499                 Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
500                 true,
501                 new ContentObserver(mHandler) {
502                     @Override
503                     public void onChange(boolean selfChange) {
504                         updateEnabled();
505                     }
506                 }, UserHandle.USER_ALL);
507 
508         mHandler.post(this::handleInitialize);
509         mHandler.post(mGnssSatelliteBlocklistHelper::updateSatelliteBlocklist);
510     }
511 
handleInitialize()512     private void handleInitialize() {
513         if (mGnssNative.isGnssVisibilityControlSupported()) {
514             mGnssVisibilityControl = new GnssVisibilityControl(mContext, mHandler.getLooper(),
515                     mNIHandler);
516         }
517 
518         // load default GPS configuration
519         // (this configuration might change in the future based on SIM changes)
520         reloadGpsProperties();
521 
522         // listen for events
523         IntentFilter intentFilter = new IntentFilter();
524         intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
525         intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
526         mContext.registerReceiver(new BroadcastReceiver() {
527             @Override
528             public void onReceive(Context context, Intent intent) {
529                 String action = intent.getAction();
530                 if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
531                 if (action == null) {
532                     return;
533                 }
534 
535                 switch (action) {
536                     case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
537                     case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
538                         subscriptionOrCarrierConfigChanged();
539                         break;
540                 }
541             }
542         }, intentFilter, null, mHandler);
543 
544         mNetworkConnectivityHandler.registerNetworkCallbacks();
545 
546         // permanently passively listen to all network locations
547         LocationManager locationManager = Objects.requireNonNull(
548                 mContext.getSystemService(LocationManager.class));
549         if (locationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
550             locationManager.requestLocationUpdates(
551                     LocationManager.NETWORK_PROVIDER,
552                     new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL)
553                             .setMinUpdateIntervalMillis(0)
554                             .setHiddenFromAppOps(true)
555                             .build(),
556                     DIRECT_EXECUTOR,
557                     this::injectLocation);
558         }
559 
560         updateEnabled();
561     }
562 
563     /**
564      * Implements {@link InjectNtpTimeCallback#injectTime}
565      */
566     @Override
injectTime(long time, long timeReference, int uncertainty)567     public void injectTime(long time, long timeReference, int uncertainty) {
568         mGnssNative.injectTime(time, timeReference, uncertainty);
569     }
570 
571     /**
572      * Implements {@link GnssNetworkConnectivityHandler.GnssNetworkListener#onNetworkAvailable()}
573      */
onNetworkAvailable()574     private void onNetworkAvailable() {
575         mNtpTimeHelper.onNetworkAvailable();
576         // Download only if supported, (prevents an unnecessary on-boot download)
577         if (mSupportsPsds) {
578             synchronized (mLock) {
579                 for (int psdsType : mPendingDownloadPsdsTypes) {
580                     postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
581                 }
582                 mPendingDownloadPsdsTypes.clear();
583             }
584         }
585     }
586 
handleRequestLocation(boolean independentFromGnss, boolean isUserEmergency)587     private void handleRequestLocation(boolean independentFromGnss, boolean isUserEmergency) {
588         if (isRequestLocationRateLimited()) {
589             if (DEBUG) {
590                 Log.d(TAG, "RequestLocation is denied due to too frequent requests.");
591             }
592             return;
593         }
594         ContentResolver resolver = mContext.getContentResolver();
595         long durationMillis = Settings.Global.getLong(
596                 resolver,
597                 Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS,
598                 LOCATION_UPDATE_DURATION_MILLIS);
599         if (durationMillis == 0) {
600             Log.i(TAG, "GNSS HAL location request is disabled by Settings.");
601             return;
602         }
603 
604         LocationManager locationManager = (LocationManager) mContext.getSystemService(
605                 Context.LOCATION_SERVICE);
606         String provider;
607         LocationListener locationListener;
608         LocationRequest.Builder locationRequest = new LocationRequest.Builder(
609                 LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS).setMaxUpdates(1);
610 
611         if (independentFromGnss) {
612             // For fast GNSS TTFF - we use an empty listener because we will rely on the passive
613             // network listener to actually inject the location. this prevents double injection
614             provider = LocationManager.NETWORK_PROVIDER;
615             locationListener = location -> { };
616             locationRequest.setQuality(LocationRequest.QUALITY_LOW_POWER);
617         } else {
618             // For Device-Based Hybrid (E911)
619             provider = LocationManager.FUSED_PROVIDER;
620             locationListener = this::injectBestLocation;
621             locationRequest.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY);
622         }
623 
624         // Ignore location settings if in emergency mode. This is only allowed for
625         // isUserEmergency request (introduced in HAL v2.0), or HAL v1.1.
626         if (mNIHandler.getInEmergency()) {
627             GnssConfiguration.HalInterfaceVersion halVersion =
628                     mGnssConfiguration.getHalInterfaceVersion();
629             if (isUserEmergency || halVersion.mMajor < 2) {
630                 locationRequest.setLocationSettingsIgnored(true);
631                 durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
632             }
633         }
634 
635         locationRequest.setDurationMillis(durationMillis);
636 
637         Log.i(TAG,
638                 String.format(
639                         "GNSS HAL Requesting location updates from %s provider for %d millis.",
640                         provider, durationMillis));
641 
642         if (locationManager.getProvider(provider) != null) {
643             locationManager.requestLocationUpdates(provider, locationRequest.build(),
644                     DIRECT_EXECUTOR, locationListener);
645         }
646     }
647 
injectBestLocation(Location location)648     private void injectBestLocation(Location location) {
649         if (DEBUG) {
650             Log.d(TAG, "injectBestLocation: " + location);
651         }
652 
653         if (location.isMock()) {
654             return;
655         }
656 
657         mGnssNative.injectBestLocation(location);
658     }
659 
660     /** Returns true if the location request is too frequent. */
isRequestLocationRateLimited()661     private boolean isRequestLocationRateLimited() {
662         // TODO: implement exponential backoff.
663         return false;
664     }
665 
handleDownloadPsdsData(int psdsType)666     private void handleDownloadPsdsData(int psdsType) {
667         if (!mSupportsPsds) {
668             // native code reports psds not supported, don't try
669             Log.d(TAG, "handleDownloadPsdsData() called when PSDS not supported");
670             return;
671         }
672         if (!mNetworkConnectivityHandler.isDataNetworkConnected()) {
673             // try again when network is up
674             synchronized (mLock) {
675                 mPendingDownloadPsdsTypes.add(psdsType);
676             }
677             return;
678         }
679         synchronized (mLock) {
680             // hold wake lock while task runs
681             mDownloadPsdsWakeLock.acquire(DOWNLOAD_PSDS_DATA_TIMEOUT_MS);
682         }
683         Log.i(TAG, "WakeLock acquired by handleDownloadPsdsData()");
684         Executors.newSingleThreadExecutor().execute(() -> {
685             GnssPsdsDownloader psdsDownloader = new GnssPsdsDownloader(
686                     mGnssConfiguration.getProperties());
687             byte[] data = psdsDownloader.downloadPsdsData(psdsType);
688             if (data != null) {
689                 mHandler.post(() -> {
690                     FrameworkStatsLog.write(FrameworkStatsLog.GNSS_PSDS_DOWNLOAD_REPORTED,
691                             psdsType);
692                     if (DEBUG) Log.d(TAG, "calling native_inject_psds_data");
693                     mGnssNative.injectPsdsData(data, data.length, psdsType);
694                     synchronized (mLock) {
695                         mPsdsBackOff.reset();
696                     }
697                 });
698                 PackageManager pm = mContext.getPackageManager();
699                 if (pm != null && pm.hasSystemFeature(FEATURE_WATCH)
700                         && psdsType == GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX
701                         && mGnssConfiguration.isPsdsPeriodicDownloadEnabled()) {
702                     if (DEBUG) Log.d(TAG, "scheduling next long term Psds download");
703                     mHandler.removeCallbacksAndMessages(mPsdsPeriodicDownloadToken);
704                     mHandler.postDelayed(() -> handleDownloadPsdsData(psdsType),
705                             mPsdsPeriodicDownloadToken,
706                             GnssPsdsDownloader.PSDS_INTERVAL);
707                 }
708             } else {
709                 // Try download PSDS data again later according to backoff time.
710                 // Since this is delayed and not urgent, we do not hold a wake lock here.
711                 // The arg2 below should not be 1 otherwise the wakelock will be under-locked.
712                 long backoffMillis;
713                 synchronized (mLock) {
714                     backoffMillis = mPsdsBackOff.nextBackoffMillis();
715                 }
716                 mHandler.postDelayed(() -> handleDownloadPsdsData(psdsType), backoffMillis);
717             }
718 
719             // Release wake lock held by task, synchronize on mLock in case multiple
720             // download tasks overrun.
721             synchronized (mLock) {
722                 if (mDownloadPsdsWakeLock.isHeld()) {
723                     // This wakelock may have time-out, if a timeout was specified.
724                     // Catch (and ignore) any timeout exceptions.
725                     mDownloadPsdsWakeLock.release();
726                     if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadPsdsData()");
727                 } else {
728                     Log.e(TAG, "WakeLock expired before release in "
729                             + "handleDownloadPsdsData()");
730                 }
731             }
732         });
733     }
734 
injectLocation(Location location)735     private void injectLocation(Location location) {
736         if (!location.isMock()) {
737             mGnssNative.injectLocation(location);
738         }
739     }
740 
setSuplHostPort()741     private void setSuplHostPort() {
742         mSuplServerHost = mGnssConfiguration.getSuplHost();
743         mSuplServerPort = mGnssConfiguration.getSuplPort(TCP_MIN_PORT);
744         if (mSuplServerHost != null
745                 && mSuplServerPort > TCP_MIN_PORT
746                 && mSuplServerPort <= TCP_MAX_PORT) {
747             mGnssNative.setAgpsServer(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
748                     mSuplServerHost, mSuplServerPort);
749         }
750     }
751 
752     /**
753      * Checks what SUPL mode to use, according to the AGPS mode as well as the
754      * allowed mode from properties.
755      *
756      * @param agpsEnabled whether AGPS is enabled by settings value
757      * @return SUPL mode (MSA vs MSB vs STANDALONE)
758      */
getSuplMode(boolean agpsEnabled)759     private int getSuplMode(boolean agpsEnabled) {
760         if (agpsEnabled) {
761             int suplMode = mGnssConfiguration.getSuplMode(0);
762             if (suplMode == 0) {
763                 return GNSS_POSITION_MODE_STANDALONE;
764             }
765 
766             // MS-Based is the preferred mode for Assisted-GPS position computation, so we favor
767             // such mode when it is available
768             if (mGnssNative.getCapabilities().hasMsb() && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
769                 return GNSS_POSITION_MODE_MS_BASED;
770             }
771         }
772         return GNSS_POSITION_MODE_STANDALONE;
773     }
774 
setGpsEnabled(boolean enabled)775     private void setGpsEnabled(boolean enabled) {
776         synchronized (mLock) {
777             mGpsEnabled = enabled;
778         }
779     }
780 
781     /**
782      * Set whether the GnssLocationProvider is suspended. This method was added to help support
783      * power management use cases on automotive devices.
784      */
setAutomotiveGnssSuspended(boolean suspended)785     public void setAutomotiveGnssSuspended(boolean suspended) {
786         synchronized (mLock) {
787             mAutomotiveSuspend = suspended;
788         }
789         mHandler.post(this::updateEnabled);
790     }
791 
792     /**
793      * Return whether the GnssLocationProvider is suspended or not. This method was added to help
794      * support power management use cases on automotive devices.
795      */
isAutomotiveGnssSuspended()796     public boolean isAutomotiveGnssSuspended() {
797         synchronized (mLock) {
798             return mAutomotiveSuspend && !mGpsEnabled;
799         }
800     }
801 
handleEnable()802     private void handleEnable() {
803         if (DEBUG) Log.d(TAG, "handleEnable");
804 
805         boolean inited = mGnssNative.init();
806 
807         if (inited) {
808             setGpsEnabled(true);
809             mSupportsPsds = mGnssNative.isPsdsSupported();
810 
811             // TODO: remove the following native calls if we can make sure they are redundant.
812             if (mSuplServerHost != null) {
813                 mGnssNative.setAgpsServer(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
814                         mSuplServerHost, mSuplServerPort);
815             }
816             if (mC2KServerHost != null) {
817                 mGnssNative.setAgpsServer(GnssNetworkConnectivityHandler.AGPS_TYPE_C2K,
818                         mC2KServerHost, mC2KServerPort);
819             }
820 
821             mBatchingEnabled = mGnssNative.initBatching() && mGnssNative.getBatchSize() > 1;
822             if (mGnssVisibilityControl != null) {
823                 mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ true);
824             }
825         } else {
826             setGpsEnabled(false);
827             Log.w(TAG, "Failed to enable location provider");
828         }
829     }
830 
handleDisable()831     private void handleDisable() {
832         if (DEBUG) Log.d(TAG, "handleDisable");
833 
834         setGpsEnabled(false);
835         updateClientUids(new WorkSource());
836         stopNavigating();
837         stopBatching();
838 
839         if (mGnssVisibilityControl != null) {
840             mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ false);
841         }
842         // do this before releasing wakelock
843         mGnssNative.cleanupBatching();
844         mGnssNative.cleanup();
845     }
846 
updateEnabled()847     private void updateEnabled() {
848         // Generally follow location setting for current user
849         boolean enabled = mContext.getSystemService(LocationManager.class)
850                 .isLocationEnabledForUser(UserHandle.CURRENT);
851 
852         // .. but enable anyway, if there's an active bypass request (e.g. ELS or ADAS)
853         enabled |= (mProviderRequest != null
854                 && mProviderRequest.isActive()
855                 && mProviderRequest.isBypass());
856 
857         // .. disable if automotive device needs to go into suspend
858         synchronized (mLock) {
859             enabled &= !mAutomotiveSuspend;
860         }
861 
862         // ... and, finally, disable anyway, if device is being shut down
863         enabled &= !mShutdown;
864 
865         if (enabled == isGpsEnabled()) {
866             return;
867         }
868 
869         if (enabled) {
870             handleEnable();
871         } else {
872             handleDisable();
873         }
874     }
875 
isGpsEnabled()876     private boolean isGpsEnabled() {
877         synchronized (mLock) {
878             return mGpsEnabled;
879         }
880     }
881 
882     /**
883      * Returns the hardware batch size available in this hardware implementation. If the available
884      * size is variable, for example, based on other operations consuming memory, this is the
885      * minimum size guaranteed to be available for batching operations.
886      */
getBatchSize()887     public int getBatchSize() {
888         return mGnssNative.getBatchSize();
889     }
890 
891     @Override
onFlush(Runnable listener)892     protected void onFlush(Runnable listener) {
893         boolean added = false;
894         synchronized (mLock) {
895             if (mBatchingEnabled) {
896                 added = mFlushListeners.add(listener);
897             }
898         }
899         if (!added) {
900             listener.run();
901         } else {
902             mGnssNative.flushBatch();
903         }
904     }
905 
906     @Override
onSetRequest(ProviderRequest request)907     public void onSetRequest(ProviderRequest request) {
908         mProviderRequest = request;
909         updateEnabled();
910         updateRequirements();
911     }
912 
913     // Called when the requirements for GPS may have changed
updateRequirements()914     private void updateRequirements() {
915         if (mProviderRequest == null || mProviderRequest.getWorkSource() == null) {
916             return;
917         }
918 
919         if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
920         if (mProviderRequest.isActive() && isGpsEnabled()) {
921             // update client uids
922             updateClientUids(mProviderRequest.getWorkSource());
923 
924             if (mProviderRequest.getIntervalMillis() <= Integer.MAX_VALUE) {
925                 mFixInterval = (int) mProviderRequest.getIntervalMillis();
926             } else {
927                 Log.w(TAG, "interval overflow: " + mProviderRequest.getIntervalMillis());
928                 mFixInterval = Integer.MAX_VALUE;
929             }
930 
931             int batchIntervalMs = max(mFixInterval, MIN_BATCH_INTERVAL_MS);
932             long batchLengthMs = Math.min(mProviderRequest.getMaxUpdateDelayMillis(),
933                     MAX_BATCH_LENGTH_MS);
934 
935             // apply request to GPS engine
936             if (mBatchingEnabled && batchLengthMs / 2 >= batchIntervalMs) {
937                 stopNavigating();
938                 mFixInterval = batchIntervalMs;
939                 startBatching(batchLengthMs);
940             } else {
941                 stopBatching();
942 
943                 if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
944                     // change period and/or lowPowerMode
945                     if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
946                             mFixInterval, mProviderRequest.isLowPower())) {
947                         Log.e(TAG, "set_position_mode failed in updateRequirements");
948                     }
949                 } else if (!mStarted) {
950                     // start GPS
951                     startNavigating();
952                 } else {
953                     // GNSS Engine is already ON, but no GPS_CAPABILITY_SCHEDULING
954                     mAlarmManager.cancel(mTimeoutListener);
955                     if (mFixInterval >= NO_FIX_TIMEOUT) {
956                         // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
957                         // and our fix interval is not short
958                         mAlarmManager.set(ELAPSED_REALTIME_WAKEUP,
959                                 SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, TAG,
960                                 mTimeoutListener, mHandler);
961                     }
962                 }
963             }
964         } else {
965             updateClientUids(new WorkSource());
966             stopNavigating();
967             stopBatching();
968         }
969     }
970 
setPositionMode(int mode, int recurrence, int minInterval, boolean lowPowerMode)971     private boolean setPositionMode(int mode, int recurrence, int minInterval,
972             boolean lowPowerMode) {
973         GnssPositionMode positionMode = new GnssPositionMode(mode, recurrence, minInterval,
974                 0, 0, lowPowerMode);
975         if (mLastPositionMode != null && mLastPositionMode.equals(positionMode)) {
976             return true;
977         }
978 
979         boolean result = mGnssNative.setPositionMode(mode, recurrence, minInterval, 0, 0,
980                 lowPowerMode);
981         if (result) {
982             mLastPositionMode = positionMode;
983         } else {
984             mLastPositionMode = null;
985         }
986         return result;
987     }
988 
updateClientUids(WorkSource source)989     private void updateClientUids(WorkSource source) {
990         if (source.equals(mClientSource)) {
991             return;
992         }
993 
994         // (1) Inform BatteryStats that the list of IDs we're tracking changed.
995         try {
996             mBatteryStats.noteGpsChanged(mClientSource, source);
997         } catch (RemoteException e) {
998             Log.w(TAG, "RemoteException", e);
999         }
1000 
1001         // (2) Inform AppOps service about the list of changes to UIDs.
1002 
1003         // TODO: this doesn't seem correct, work chain attribution tag != package?
1004         List<WorkChain>[] diffs = WorkSource.diffChains(mClientSource, source);
1005         if (diffs != null) {
1006             List<WorkChain> newChains = diffs[0];
1007             List<WorkChain> goneChains = diffs[1];
1008 
1009             if (newChains != null) {
1010                 for (WorkChain newChain : newChains) {
1011                     mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
1012                             newChain.getAttributionTag());
1013                 }
1014             }
1015 
1016             if (goneChains != null) {
1017                 for (WorkChain goneChain : goneChains) {
1018                     mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
1019                             goneChain.getAttributionTag());
1020                 }
1021             }
1022 
1023             mClientSource.transferWorkChains(source);
1024         }
1025 
1026         // Update the flat UIDs and names list and inform app-ops of all changes.
1027         // TODO: why is GnssLocationProvider the only component using these deprecated APIs?
1028         WorkSource[] changes = mClientSource.setReturningDiffs(source);
1029         if (changes != null) {
1030             WorkSource newWork = changes[0];
1031             WorkSource goneWork = changes[1];
1032 
1033             // Update sources that were not previously tracked.
1034             if (newWork != null) {
1035                 for (int i = 0; i < newWork.size(); i++) {
1036                     mAppOps.startOpNoThrow(AppOpsManager.OP_GPS,
1037                             newWork.getUid(i), newWork.getPackageName(i));
1038                 }
1039             }
1040 
1041             // Update sources that are no longer tracked.
1042             if (goneWork != null) {
1043                 for (int i = 0; i < goneWork.size(); i++) {
1044                     mAppOps.finishOp(AppOpsManager.OP_GPS, goneWork.getUid(i),
1045                             goneWork.getPackageName(i));
1046                 }
1047             }
1048         }
1049     }
1050 
1051     @Override
onExtraCommand(int uid, int pid, String command, Bundle extras)1052     public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
1053         if ("delete_aiding_data".equals(command)) {
1054             deleteAidingData(extras);
1055         } else if ("force_time_injection".equals(command)) {
1056             requestUtcTime();
1057         } else if ("force_psds_injection".equals(command)) {
1058             if (mSupportsPsds) {
1059                 postWithWakeLockHeld(() -> handleDownloadPsdsData(
1060                         GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
1061             }
1062         } else if ("request_power_stats".equals(command)) {
1063             mGnssNative.requestPowerStats();
1064         } else {
1065             Log.w(TAG, "sendExtraCommand: unknown command " + command);
1066         }
1067     }
1068 
deleteAidingData(Bundle extras)1069     private void deleteAidingData(Bundle extras) {
1070         int flags;
1071 
1072         if (extras == null) {
1073             flags = GNSS_AIDING_TYPE_ALL;
1074         } else {
1075             flags = 0;
1076             if (extras.getBoolean("ephemeris")) flags |= GNSS_AIDING_TYPE_EPHEMERIS;
1077             if (extras.getBoolean("almanac")) flags |= GNSS_AIDING_TYPE_ALMANAC;
1078             if (extras.getBoolean("position")) flags |= GNSS_AIDING_TYPE_POSITION;
1079             if (extras.getBoolean("time")) flags |= GNSS_AIDING_TYPE_TIME;
1080             if (extras.getBoolean("iono")) flags |= GNSS_AIDING_TYPE_IONO;
1081             if (extras.getBoolean("utc")) flags |= GNSS_AIDING_TYPE_UTC;
1082             if (extras.getBoolean("health")) flags |= GNSS_AIDING_TYPE_HEALTH;
1083             if (extras.getBoolean("svdir")) flags |= GNSS_AIDING_TYPE_SVDIR;
1084             if (extras.getBoolean("svsteer")) flags |= GNSS_AIDING_TYPE_SVSTEER;
1085             if (extras.getBoolean("sadata")) flags |= GNSS_AIDING_TYPE_SADATA;
1086             if (extras.getBoolean("rti")) flags |= GNSS_AIDING_TYPE_RTI;
1087             if (extras.getBoolean("celldb-info")) flags |= GNSS_AIDING_TYPE_CELLDB_INFO;
1088             if (extras.getBoolean("all")) flags |= GNSS_AIDING_TYPE_ALL;
1089         }
1090 
1091         if (flags != 0) {
1092             mGnssNative.deleteAidingData(flags);
1093         }
1094     }
1095 
startNavigating()1096     private void startNavigating() {
1097         if (!mStarted) {
1098             if (DEBUG) Log.d(TAG, "startNavigating");
1099             mTimeToFirstFix = 0;
1100             mLastFixTime = 0;
1101             setStarted(true);
1102             mPositionMode = GNSS_POSITION_MODE_STANDALONE;
1103 
1104             boolean agpsEnabled =
1105                     (Settings.Global.getInt(mContext.getContentResolver(),
1106                             Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
1107             mPositionMode = getSuplMode(agpsEnabled);
1108 
1109             if (DEBUG) {
1110                 String mode;
1111 
1112                 switch (mPositionMode) {
1113                     case GNSS_POSITION_MODE_STANDALONE:
1114                         mode = "standalone";
1115                         break;
1116                     case GNSS_POSITION_MODE_MS_ASSISTED:
1117                         mode = "MS_ASSISTED";
1118                         break;
1119                     case GNSS_POSITION_MODE_MS_BASED:
1120                         mode = "MS_BASED";
1121                         break;
1122                     default:
1123                         mode = "unknown";
1124                         break;
1125                 }
1126                 Log.d(TAG, "setting position_mode to " + mode);
1127             }
1128 
1129             int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
1130             if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
1131                     interval, mProviderRequest.isLowPower())) {
1132                 setStarted(false);
1133                 Log.e(TAG, "set_position_mode failed in startNavigating()");
1134                 return;
1135             }
1136             if (!mGnssNative.start()) {
1137                 setStarted(false);
1138                 Log.e(TAG, "native_start failed in startNavigating()");
1139                 return;
1140             }
1141 
1142             // reset SV count to zero
1143             mLocationExtras.reset();
1144             mFixRequestTime = SystemClock.elapsedRealtime();
1145             if (!mGnssNative.getCapabilities().hasScheduling()) {
1146                 // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
1147                 // and our fix interval is not short
1148                 if (mFixInterval >= NO_FIX_TIMEOUT) {
1149                     mAlarmManager.set(ELAPSED_REALTIME_WAKEUP,
1150                             SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, TAG, mTimeoutListener,
1151                             mHandler);
1152                 }
1153             }
1154         }
1155     }
1156 
stopNavigating()1157     private void stopNavigating() {
1158         if (DEBUG) Log.d(TAG, "stopNavigating");
1159         if (mStarted) {
1160             setStarted(false);
1161             mGnssNative.stop();
1162             mLastFixTime = 0;
1163             // native_stop() may reset the position mode in hardware.
1164             mLastPositionMode = null;
1165 
1166             // reset SV count to zero
1167             mLocationExtras.reset();
1168         }
1169         mAlarmManager.cancel(mTimeoutListener);
1170         mAlarmManager.cancel(mWakeupListener);
1171     }
1172 
startBatching(long batchLengthMs)1173     private void startBatching(long batchLengthMs) {
1174         long batchSize = batchLengthMs / mFixInterval;
1175 
1176         if (DEBUG) {
1177             Log.d(TAG, "startBatching " + mFixInterval + " " + batchLengthMs);
1178         }
1179         if (mGnssNative.startBatch(MILLISECONDS.toNanos(mFixInterval), 0, true)) {
1180             mBatchingStarted = true;
1181 
1182             if (batchSize < getBatchSize()) {
1183                 // if the batch size is smaller than the hardware batch size, use an alarm to flush
1184                 // locations as appropriate
1185                 mBatchingAlarm = () -> {
1186                     boolean flush = false;
1187                     synchronized (mLock) {
1188                         if (mBatchingAlarm != null) {
1189                             flush = true;
1190                             mAlarmManager.setExact(ELAPSED_REALTIME_WAKEUP,
1191                                     SystemClock.elapsedRealtime() + batchLengthMs, TAG,
1192                                     mBatchingAlarm, FgThread.getHandler());
1193                         }
1194                     }
1195 
1196                     if (flush) {
1197                         mGnssNative.flushBatch();
1198                     }
1199                 };
1200                 mAlarmManager.setExact(ELAPSED_REALTIME_WAKEUP,
1201                         SystemClock.elapsedRealtime() + batchLengthMs, TAG,
1202                         mBatchingAlarm, FgThread.getHandler());
1203             }
1204         } else {
1205             Log.e(TAG, "native_start_batch failed in startBatching()");
1206         }
1207     }
1208 
stopBatching()1209     private void stopBatching() {
1210         if (DEBUG) Log.d(TAG, "stopBatching");
1211         if (mBatchingStarted) {
1212             if (mBatchingAlarm != null) {
1213                 mAlarmManager.cancel(mBatchingAlarm);
1214                 mBatchingAlarm = null;
1215             }
1216             mGnssNative.flushBatch();
1217             mGnssNative.stopBatch();
1218             mBatchingStarted = false;
1219         }
1220     }
1221 
setStarted(boolean started)1222     private void setStarted(boolean started) {
1223         if (mStarted != started) {
1224             mStarted = started;
1225             mStartedChangedElapsedRealtime = SystemClock.elapsedRealtime();
1226         }
1227     }
1228 
hibernate()1229     private void hibernate() {
1230         // stop GPS until our next fix interval arrives
1231         stopNavigating();
1232         long now = SystemClock.elapsedRealtime();
1233         mAlarmManager.set(ELAPSED_REALTIME_WAKEUP, now + mFixInterval, TAG,
1234                 mWakeupListener, mHandler);
1235     }
1236 
handleReportLocation(boolean hasLatLong, Location location)1237     private void handleReportLocation(boolean hasLatLong, Location location) {
1238         if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
1239 
1240         location.setExtras(mLocationExtras.getBundle());
1241 
1242         reportLocation(LocationResult.wrap(location).validate());
1243 
1244         if (mStarted) {
1245             mGnssMetrics.logReceivedLocationStatus(hasLatLong);
1246             if (hasLatLong) {
1247                 if (location.hasAccuracy()) {
1248                     mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
1249                 }
1250                 if (mTimeToFirstFix > 0) {
1251                     int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
1252                     mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
1253                 }
1254             }
1255         } else {
1256             // Warn or error about long delayed GNSS engine shutdown as this generally wastes
1257             // power and sends location when not expected.
1258             long locationAfterStartedFalseMillis =
1259                     SystemClock.elapsedRealtime() - mStartedChangedElapsedRealtime;
1260             if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS) {
1261                 String logMessage = "Unexpected GNSS Location report "
1262                         + TimeUtils.formatDuration(locationAfterStartedFalseMillis)
1263                         + " after location turned off";
1264                 if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS) {
1265                     Log.e(TAG, logMessage);
1266                 } else {
1267                     Log.w(TAG, logMessage);
1268                 }
1269             }
1270         }
1271 
1272         mLastFixTime = SystemClock.elapsedRealtime();
1273         // report time to first fix
1274         if (mTimeToFirstFix == 0 && hasLatLong) {
1275             mTimeToFirstFix = (int) (mLastFixTime - mFixRequestTime);
1276             if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
1277             if (mStarted) {
1278                 mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
1279             }
1280         }
1281 
1282         if (mStarted) {
1283             // For devices that use framework scheduling, a timer may be set to ensure we don't
1284             // spend too much power searching for a location, when the requested update rate is
1285             // slow.
1286             // As we just recievied a location, we'll cancel that timer.
1287             if (!mGnssNative.getCapabilities().hasScheduling() && mFixInterval < NO_FIX_TIMEOUT) {
1288                 mAlarmManager.cancel(mTimeoutListener);
1289             }
1290         }
1291 
1292         if (!mGnssNative.getCapabilities().hasScheduling() && mStarted
1293                 && mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
1294             if (DEBUG) Log.d(TAG, "got fix, hibernating");
1295             hibernate();
1296         }
1297     }
1298 
handleReportSvStatus(GnssStatus gnssStatus)1299     private void handleReportSvStatus(GnssStatus gnssStatus) {
1300         // Log CN0 as part of GNSS metrics
1301         mGnssMetrics.logCn0(gnssStatus);
1302 
1303         if (VERBOSE) {
1304             Log.v(TAG, "SV count: " + gnssStatus.getSatelliteCount());
1305         }
1306 
1307         int usedInFixCount = 0;
1308         int maxCn0 = 0;
1309         int meanCn0 = 0;
1310         for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) {
1311             if (gnssStatus.usedInFix(i)) {
1312                 ++usedInFixCount;
1313                 if (gnssStatus.getCn0DbHz(i) > maxCn0) {
1314                     maxCn0 = (int) gnssStatus.getCn0DbHz(i);
1315                 }
1316                 meanCn0 += gnssStatus.getCn0DbHz(i);
1317                 mGnssMetrics.logConstellationType(gnssStatus.getConstellationType(i));
1318             }
1319         }
1320         if (usedInFixCount > 0) {
1321             meanCn0 /= usedInFixCount;
1322         }
1323         // return number of sats used in fix instead of total reported
1324         mLocationExtras.set(usedInFixCount, meanCn0, maxCn0);
1325 
1326         mGnssMetrics.logSvStatus(gnssStatus);
1327     }
1328 
restartLocationRequest()1329     private void restartLocationRequest() {
1330         if (DEBUG) Log.d(TAG, "restartLocationRequest");
1331         setStarted(false);
1332         updateRequirements();
1333     }
1334 
1335     //=============================================================
1336     // NI Client support
1337     //=============================================================
1338     private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
1339         // Sends a response for an NI request to HAL.
1340         @Override
1341         public boolean sendNiResponse(int notificationId, int userResponse) {
1342             // TODO Add Permission check
1343 
1344             if (DEBUG) {
1345                 Log.d(TAG, "sendNiResponse, notifId: " + notificationId
1346                         + ", response: " + userResponse);
1347             }
1348             mGnssNative.sendNiResponse(notificationId, userResponse);
1349 
1350             FrameworkStatsLog.write(FrameworkStatsLog.GNSS_NI_EVENT_REPORTED,
1351                     FrameworkStatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE,
1352                     notificationId,
1353                     /* niType= */ 0,
1354                     /* needNotify= */ false,
1355                     /* needVerify= */ false,
1356                     /* privacyOverride= */ false,
1357                     /* timeout= */ 0,
1358                     /* defaultResponse= */ 0,
1359                     /* requestorId= */ null,
1360                     /* text= */ null,
1361                     /* requestorIdEncoding= */ 0,
1362                     /* textEncoding= */ 0,
1363                     mSuplEsEnabled,
1364                     isGpsEnabled(),
1365                     userResponse);
1366 
1367             return true;
1368         }
1369     };
1370 
getNetInitiatedListener()1371     public INetInitiatedListener getNetInitiatedListener() {
1372         return mNetInitiatedListener;
1373     }
1374 
1375     /** Reports a NI notification. */
reportNiNotification(int notificationId, int niType, int notifyFlags, int timeout, int defaultResponse, String requestorId, String text, int requestorIdEncoding, int textEncoding)1376     private void reportNiNotification(int notificationId, int niType, int notifyFlags, int timeout,
1377             int defaultResponse, String requestorId, String text, int requestorIdEncoding,
1378             int textEncoding) {
1379         Log.i(TAG, "reportNiNotification: entered");
1380         Log.i(TAG, "notificationId: " + notificationId
1381                 + ", niType: " + niType
1382                 + ", notifyFlags: " + notifyFlags
1383                 + ", timeout: " + timeout
1384                 + ", defaultResponse: " + defaultResponse);
1385 
1386         Log.i(TAG, "requestorId: " + requestorId
1387                 + ", text: " + text
1388                 + ", requestorIdEncoding: " + requestorIdEncoding
1389                 + ", textEncoding: " + textEncoding);
1390 
1391         GpsNiNotification notification = new GpsNiNotification();
1392 
1393         notification.notificationId = notificationId;
1394         notification.niType = niType;
1395         notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
1396         notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
1397         notification.privacyOverride =
1398                 (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
1399         notification.timeout = timeout;
1400         notification.defaultResponse = defaultResponse;
1401         notification.requestorId = requestorId;
1402         notification.text = text;
1403         notification.requestorIdEncoding = requestorIdEncoding;
1404         notification.textEncoding = textEncoding;
1405 
1406         mNIHandler.handleNiNotification(notification);
1407         FrameworkStatsLog.write(FrameworkStatsLog.GNSS_NI_EVENT_REPORTED,
1408                 FrameworkStatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_REQUEST,
1409                 notification.notificationId,
1410                 notification.niType,
1411                 notification.needNotify,
1412                 notification.needVerify,
1413                 notification.privacyOverride,
1414                 notification.timeout,
1415                 notification.defaultResponse,
1416                 notification.requestorId,
1417                 notification.text,
1418                 notification.requestorIdEncoding,
1419                 notification.textEncoding,
1420                 mSuplEsEnabled,
1421                 isGpsEnabled(),
1422                 /* userResponse= */ 0);
1423     }
1424 
requestUtcTime()1425     private void requestUtcTime() {
1426         if (DEBUG) Log.d(TAG, "utcTimeRequest");
1427         postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
1428     }
1429 
1430 
getCellType(CellInfo ci)1431     private static int getCellType(CellInfo ci) {
1432         if (ci instanceof CellInfoGsm) {
1433             return CellInfo.TYPE_GSM;
1434         } else if (ci instanceof CellInfoWcdma) {
1435             return CellInfo.TYPE_WCDMA;
1436         } else if (ci instanceof CellInfoLte) {
1437             return CellInfo.TYPE_LTE;
1438         } else if (ci instanceof CellInfoNr) {
1439             return CellInfo.TYPE_NR;
1440         }
1441         return CellInfo.TYPE_UNKNOWN;
1442     }
1443 
1444     /**
1445      * Extract the CID/CI for GSM/WCDMA/LTE/NR
1446      *
1447      * @return the cell ID or -1 if invalid
1448      */
getCidFromCellIdentity(CellIdentity id)1449     private static long getCidFromCellIdentity(CellIdentity id) {
1450         if (id == null) return -1;
1451         long cid = -1;
1452         switch(id.getType()) {
1453             case CellInfo.TYPE_GSM: cid = ((CellIdentityGsm) id).getCid(); break;
1454             case CellInfo.TYPE_WCDMA: cid = ((CellIdentityWcdma) id).getCid(); break;
1455             case CellInfo.TYPE_LTE: cid = ((CellIdentityLte) id).getCi(); break;
1456             case CellInfo.TYPE_NR: cid = ((CellIdentityNr) id).getNci(); break;
1457             default: break;
1458         }
1459         // If the CID is unreported
1460         if (cid == (id.getType() == CellInfo.TYPE_NR
1461                 ? CellInfo.UNAVAILABLE_LONG : CellInfo.UNAVAILABLE)) {
1462             cid = -1;
1463         }
1464 
1465         return cid;
1466     }
1467 
setRefLocation(int type, CellIdentity ci)1468     private void setRefLocation(int type, CellIdentity ci) {
1469         String mcc_str = ci.getMccString();
1470         String mnc_str = ci.getMncString();
1471         int mcc = mcc_str != null ? Integer.parseInt(mcc_str) : CellInfo.UNAVAILABLE;
1472         int mnc = mnc_str != null ? Integer.parseInt(mnc_str) : CellInfo.UNAVAILABLE;
1473         int lac = CellInfo.UNAVAILABLE;
1474         int tac = CellInfo.UNAVAILABLE;
1475         int pcid = CellInfo.UNAVAILABLE;
1476         int arfcn = CellInfo.UNAVAILABLE;
1477         long cid = CellInfo.UNAVAILABLE_LONG;
1478 
1479         switch (type) {
1480             case AGPS_REF_LOCATION_TYPE_GSM_CELLID:
1481                 CellIdentityGsm cig = (CellIdentityGsm) ci;
1482                 cid = cig.getCid();
1483                 lac = cig.getLac();
1484                 break;
1485             case AGPS_REF_LOCATION_TYPE_UMTS_CELLID:
1486                 CellIdentityWcdma ciw = (CellIdentityWcdma) ci;
1487                 cid = ciw.getCid();
1488                 lac = ciw.getLac();
1489                 break;
1490             case AGPS_REF_LOCATION_TYPE_LTE_CELLID:
1491                 CellIdentityLte cil = (CellIdentityLte) ci;
1492                 cid = cil.getCi();
1493                 tac = cil.getTac();
1494                 pcid = cil.getPci();
1495                 break;
1496             case AGPS_REF_LOCATION_TYPE_NR_CELLID:
1497                 CellIdentityNr cin = (CellIdentityNr) ci;
1498                 cid = cin.getNci();
1499                 tac = cin.getTac();
1500                 pcid = cin.getPci();
1501                 arfcn = cin.getNrarfcn();
1502                 break;
1503             default:
1504         }
1505 
1506         mGnssNative.setAgpsReferenceLocationCellId(
1507                 type, mcc, mnc, lac, cid, tac, pcid, arfcn);
1508     }
1509 
requestRefLocation()1510     private void requestRefLocation() {
1511         TelephonyManager phone = (TelephonyManager)
1512                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
1513 
1514         final int phoneType = phone.getPhoneType();
1515         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
1516 
1517             List<CellInfo> cil = phone.getAllCellInfo();
1518             if (cil != null) {
1519                 HashMap<Integer, CellIdentity> cellIdentityMap = new HashMap<>();
1520                 cil.sort(Comparator.comparingInt(
1521                         (CellInfo ci) -> ci.getCellSignalStrength().getAsuLevel()).reversed());
1522 
1523                 for (CellInfo ci : cil) {
1524                     int status = ci.getCellConnectionStatus();
1525                     if (status == CellInfo.CONNECTION_PRIMARY_SERVING
1526                             || status == CellInfo.CONNECTION_SECONDARY_SERVING) {
1527                         CellIdentity c = ci.getCellIdentity();
1528                         int t = getCellType(ci);
1529                         if (getCidFromCellIdentity(c) != -1
1530                                 && !cellIdentityMap.containsKey(t)) {
1531                             cellIdentityMap.put(t, c);
1532                         }
1533                     }
1534                 }
1535 
1536                 if (cellIdentityMap.containsKey(CellInfo.TYPE_GSM)) {
1537                     setRefLocation(AGPS_REF_LOCATION_TYPE_GSM_CELLID,
1538                             cellIdentityMap.get(CellInfo.TYPE_GSM));
1539                 } else if (cellIdentityMap.containsKey(CellInfo.TYPE_WCDMA)) {
1540                     setRefLocation(AGPS_REF_LOCATION_TYPE_UMTS_CELLID,
1541                             cellIdentityMap.get(CellInfo.TYPE_WCDMA));
1542                 } else if (cellIdentityMap.containsKey(CellInfo.TYPE_LTE)) {
1543                     setRefLocation(AGPS_REF_LOCATION_TYPE_LTE_CELLID,
1544                             cellIdentityMap.get(CellInfo.TYPE_LTE));
1545                 } else if (cellIdentityMap.containsKey(CellInfo.TYPE_NR)) {
1546                     setRefLocation(AGPS_REF_LOCATION_TYPE_NR_CELLID,
1547                             cellIdentityMap.get(CellInfo.TYPE_NR));
1548                 } else {
1549                     Log.e(TAG, "No available serving cell information.");
1550                 }
1551             } else {
1552                 Log.e(TAG, "Error getting cell location info.");
1553             }
1554         } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
1555             Log.e(TAG, "CDMA not supported.");
1556         }
1557     }
1558 
postWithWakeLockHeld(Runnable runnable)1559     private void postWithWakeLockHeld(Runnable runnable) {
1560         // hold a wake lock until this message is delivered
1561         // note that this assumes the message will not be removed from the queue before
1562         // it is handled (otherwise the wake lock would be leaked).
1563         mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
1564         boolean success = mHandler.post(() -> {
1565             try {
1566                 runnable.run();
1567             } finally {
1568                 mWakeLock.release();
1569             }
1570         });
1571         if (!success) {
1572             mWakeLock.release();
1573         }
1574     }
1575 
1576     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1577     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1578         boolean dumpAll = false;
1579 
1580         int opti = 0;
1581         while (opti < args.length) {
1582             String opt = args[opti];
1583             if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
1584                 break;
1585             }
1586             opti++;
1587             if ("-a".equals(opt)) {
1588                 dumpAll = true;
1589                 break;
1590             }
1591         }
1592 
1593         pw.print("mStarted=" + mStarted + "   (changed ");
1594         TimeUtils.formatDuration(SystemClock.elapsedRealtime()
1595                 - mStartedChangedElapsedRealtime, pw);
1596         pw.println(" ago)");
1597         pw.println("mBatchingEnabled=" + mBatchingEnabled);
1598         pw.println("mBatchingStarted=" + mBatchingStarted);
1599         pw.println("mBatchSize=" + getBatchSize());
1600         pw.println("mFixInterval=" + mFixInterval);
1601         pw.print(mGnssMetrics.dumpGnssMetricsAsText());
1602         if (dumpAll) {
1603             pw.println("mSupportsPsds=" + mSupportsPsds);
1604             pw.println(
1605                     "PsdsServerConfigured=" + mGnssConfiguration.isLongTermPsdsServerConfigured());
1606             pw.println("native internal state: ");
1607             pw.println("  " + mGnssNative.getInternalState());
1608         }
1609     }
1610 
1611     @Override
onHalRestarted()1612     public void onHalRestarted() {
1613         reloadGpsProperties();
1614         if (isGpsEnabled()) {
1615             setGpsEnabled(false);
1616             updateEnabled();
1617             restartLocationRequest();
1618         }
1619     }
1620 
1621     @Override
onCapabilitiesChanged(GnssCapabilities oldCapabilities, GnssCapabilities newCapabilities)1622     public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
1623             GnssCapabilities newCapabilities) {
1624         mHandler.post(() -> {
1625             if (mGnssNative.getCapabilities().hasOnDemandTime()) {
1626                 mNtpTimeHelper.enablePeriodicTimeInjection();
1627                 requestUtcTime();
1628             }
1629 
1630             restartLocationRequest();
1631         });
1632     }
1633 
1634     @Override
onReportLocation(boolean hasLatLong, Location location)1635     public void onReportLocation(boolean hasLatLong, Location location) {
1636         postWithWakeLockHeld(() -> handleReportLocation(hasLatLong, location));
1637     }
1638 
1639     @Override
onReportLocations(Location[] locations)1640     public void onReportLocations(Location[] locations) {
1641         if (DEBUG) {
1642             Log.d(TAG, "Location batch of size " + locations.length + " reported");
1643         }
1644 
1645         if (locations.length > 0) {
1646             // attempt to fix up timestamps if necessary
1647             if (locations.length > 1) {
1648                 // check any realtimes outside of expected bounds
1649                 boolean fixRealtime = false;
1650                 for (int i = locations.length - 2; i >= 0; i--) {
1651                     long timeDeltaMs = locations[i + 1].getTime() - locations[i].getTime();
1652                     long realtimeDeltaMs = locations[i + 1].getElapsedRealtimeMillis()
1653                             - locations[i].getElapsedRealtimeMillis();
1654                     if (abs(timeDeltaMs - realtimeDeltaMs) > MAX_BATCH_TIMESTAMP_DELTA_MS) {
1655                         fixRealtime = true;
1656                         break;
1657                     }
1658                 }
1659 
1660                 if (fixRealtime) {
1661                     // sort for monotonically increasing time before fixing realtime - realtime will
1662                     // thus also be monotonically increasing
1663                     Arrays.sort(locations,
1664                             Comparator.comparingLong(Location::getTime));
1665 
1666                     long expectedDeltaMs =
1667                             locations[locations.length - 1].getTime()
1668                                     - locations[locations.length - 1].getElapsedRealtimeMillis();
1669                     for (int i = locations.length - 2; i >= 0; i--) {
1670                         locations[i].setElapsedRealtimeNanos(
1671                                 MILLISECONDS.toNanos(
1672                                         max(locations[i].getTime() - expectedDeltaMs, 0)));
1673                     }
1674                 } else {
1675                     // sort for monotonically increasing realttime
1676                     Arrays.sort(locations,
1677                             Comparator.comparingLong(Location::getElapsedRealtimeNanos));
1678                 }
1679             }
1680 
1681             reportLocation(LocationResult.wrap(locations).validate());
1682         }
1683 
1684         Runnable[] listeners;
1685         synchronized (mLock) {
1686             listeners = mFlushListeners.toArray(new Runnable[0]);
1687             mFlushListeners.clear();
1688         }
1689         for (Runnable listener : listeners) {
1690             listener.run();
1691         }
1692     }
1693 
1694     @Override
onReportSvStatus(GnssStatus gnssStatus)1695     public void onReportSvStatus(GnssStatus gnssStatus) {
1696         postWithWakeLockHeld(() -> handleReportSvStatus(gnssStatus));
1697     }
1698 
1699     @Override
onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr)1700     public void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
1701         mNetworkConnectivityHandler.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
1702     }
1703 
1704     @Override
onRequestPsdsDownload(int psdsType)1705     public void onRequestPsdsDownload(int psdsType) {
1706         postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
1707     }
1708 
1709     @Override
onReportNiNotification(int notificationId, int niType, int notifyFlags, int timeout, int defaultResponse, String requestorId, String text, int requestorIdEncoding, int textEncoding)1710     public void onReportNiNotification(int notificationId, int niType, int notifyFlags,
1711             int timeout, int defaultResponse, String requestorId, String text,
1712             int requestorIdEncoding, int textEncoding) {
1713         reportNiNotification(notificationId, niType, notifyFlags, timeout,
1714                 defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
1715     }
1716 
1717     @Override
onRequestSetID(@nssNative.AGpsCallbacks.AgpsSetIdFlags int flags)1718     public void onRequestSetID(@GnssNative.AGpsCallbacks.AgpsSetIdFlags int flags) {
1719         TelephonyManager phone = (TelephonyManager)
1720                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
1721         int type = AGPS_SETID_TYPE_NONE;
1722         String setId = null;
1723 
1724         int subId = SubscriptionManager.getDefaultDataSubscriptionId();
1725         if (mNIHandler.getInEmergency() && mNetworkConnectivityHandler.getActiveSubId() >= 0) {
1726             subId = mNetworkConnectivityHandler.getActiveSubId();
1727         }
1728         if (SubscriptionManager.isValidSubscriptionId(subId)) {
1729             phone = phone.createForSubscriptionId(subId);
1730         }
1731         if ((flags & AGPS_REQUEST_SETID_IMSI) == AGPS_REQUEST_SETID_IMSI) {
1732             setId = phone.getSubscriberId();
1733             if (setId != null) {
1734                 // This means the framework has the SIM card.
1735                 type = AGPS_SETID_TYPE_IMSI;
1736             }
1737         } else if ((flags & AGPS_REQUEST_SETID_MSISDN) == AGPS_REQUEST_SETID_MSISDN) {
1738             setId = phone.getLine1Number();
1739             if (setId != null) {
1740                 // This means the framework has the SIM card.
1741                 type = AGPS_SETID_TYPE_MSISDN;
1742             }
1743         }
1744 
1745         mGnssNative.setAgpsSetId(type, (setId == null) ? "" : setId);
1746     }
1747 
1748     @Override
onRequestLocation(boolean independentFromGnss, boolean isUserEmergency)1749     public void onRequestLocation(boolean independentFromGnss, boolean isUserEmergency) {
1750         if (DEBUG) {
1751             Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss
1752                     + ", isUserEmergency: "
1753                     + isUserEmergency);
1754         }
1755         postWithWakeLockHeld(() -> handleRequestLocation(independentFromGnss, isUserEmergency));
1756     }
1757 
1758     @Override
onRequestUtcTime()1759     public void onRequestUtcTime() {
1760         requestUtcTime();
1761     }
1762 
1763     @Override
onRequestRefLocation()1764     public void onRequestRefLocation() {
1765         requestRefLocation();
1766     }
1767 
1768     @Override
onReportNfwNotification(String proxyAppPackageName, byte protocolStack, String otherProtocolStackName, byte requestor, String requestorId, byte responseType, boolean inEmergencyMode, boolean isCachedLocation)1769     public void onReportNfwNotification(String proxyAppPackageName, byte protocolStack,
1770             String otherProtocolStackName, byte requestor, String requestorId,
1771             byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
1772         if (mGnssVisibilityControl == null) {
1773             Log.e(TAG, "reportNfwNotification: mGnssVisibilityControl uninitialized.");
1774             return;
1775         }
1776 
1777         mGnssVisibilityControl.reportNfwNotification(proxyAppPackageName, protocolStack,
1778                 otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
1779                 isCachedLocation);
1780     }
1781 }
1782