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