• 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.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     private boolean mNiSuplMessageListenerRegistered = false;
315 
316     private final LocationExtras mLocationExtras = new LocationExtras();
317     private final NetworkTimeHelper mNetworkTimeHelper;
318     private final GnssSatelliteBlocklistHelper mGnssSatelliteBlocklistHelper;
319 
320     // Available only on GNSS HAL 2.0 implementations and later.
321     private GnssVisibilityControl mGnssVisibilityControl;
322 
323     private final GnssNetworkConnectivityHandler mNetworkConnectivityHandler;
324     private final GpsNetInitiatedHandler mNIHandler;
325 
326     // Wakelocks
327     private final PowerManager.WakeLock mWakeLock;
328 
329     private final AlarmManager mAlarmManager;
330     private final AlarmManager.OnAlarmListener mWakeupListener = this::startNavigating;
331     private final AlarmManager.OnAlarmListener mTimeoutListener = this::hibernate;
332 
333     private final AppOpsManager mAppOps;
334     private final IBatteryStats mBatteryStats;
335 
336     @GuardedBy("mLock")
337     private final ArrayList<Runnable> mFlushListeners = new ArrayList<>(0);
338 
339     // GNSS Metrics
340     private final GnssMetrics mGnssMetrics;
341 
342     /**
343      * Implements {@link GnssSatelliteBlocklistCallback#onUpdateSatelliteBlocklist}.
344      */
345     @Override
onUpdateSatelliteBlocklist(int[] constellations, int[] svids)346     public void onUpdateSatelliteBlocklist(int[] constellations, int[] svids) {
347         mHandler.post(() -> mGnssConfiguration.setSatelliteBlocklist(constellations, svids));
348         mGnssMetrics.resetConstellationTypes();
349     }
350 
subscriptionOrCarrierConfigChanged()351     private void subscriptionOrCarrierConfigChanged() {
352         if (DEBUG) Log.d(TAG, "received SIM related action: ");
353         TelephonyManager phone = (TelephonyManager)
354                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
355         CarrierConfigManager configManager = (CarrierConfigManager)
356                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
357         int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
358         if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
359             phone = phone.createForSubscriptionId(ddSubId);
360         }
361         String mccMnc = phone.getSimOperator();
362         boolean isKeepLppProfile = false;
363         if (!TextUtils.isEmpty(mccMnc)) {
364             if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
365             if (configManager != null) {
366                 PersistableBundle b = SubscriptionManager.isValidSubscriptionId(ddSubId)
367                         ? configManager.getConfigForSubId(ddSubId) : null;
368                 if (b != null) {
369                     isKeepLppProfile =
370                             b.getBoolean(CarrierConfigManager.Gps.KEY_PERSIST_LPP_MODE_BOOL);
371                 }
372             }
373             if (isKeepLppProfile) {
374                 // load current properties for the carrier of ddSubId
375                 mGnssConfiguration.loadPropertiesFromCarrierConfig(/* inEmergency= */ false,
376                         /* activeSubId= */ -1);
377                 String lpp_profile = mGnssConfiguration.getLppProfile();
378                 // set the persist property LPP_PROFILE for the value
379                 if (lpp_profile != null) {
380                     SystemProperties.set(GnssConfiguration.LPP_PROFILE, lpp_profile);
381                 }
382             } else {
383                 // reset the persist property
384                 SystemProperties.set(GnssConfiguration.LPP_PROFILE, "");
385             }
386             reloadGpsProperties();
387         } else {
388             if (DEBUG) Log.d(TAG, "SIM MCC/MNC is still not available");
389             // Reload gnss config for no SIM case
390             mGnssConfiguration.reloadGpsProperties();
391         }
392         if (Flags.enableNiSuplMessageInjectionByCarrierConfigBugfix()) {
393             updateNiSuplMessageListenerRegistration(
394                     mGnssConfiguration.isNiSuplMessageInjectionEnabled());
395         }
396     }
397 
reloadGpsProperties()398     private void reloadGpsProperties() {
399         mGnssConfiguration.reloadGpsProperties();
400         setSuplHostPort();
401         // TODO: we should get rid of C2K specific setting.
402         mC2KServerHost = mGnssConfiguration.getC2KHost();
403         mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
404         mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
405         mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
406         if (mGnssVisibilityControl != null) {
407             mGnssVisibilityControl.onConfigurationUpdated(mGnssConfiguration);
408         }
409     }
410 
GnssLocationProvider(Context context, GnssNative gnssNative, GnssMetrics gnssMetrics)411     public GnssLocationProvider(Context context, GnssNative gnssNative,
412             GnssMetrics gnssMetrics) {
413         super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
414                 Collections.emptySet());
415 
416         mContext = context;
417         mGnssNative = gnssNative;
418         mGnssMetrics = gnssMetrics;
419 
420         // Create a wake lock
421         PowerManager powerManager = Objects.requireNonNull(
422                 mContext.getSystemService(PowerManager.class));
423         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*location*:" + TAG);
424         mWakeLock.setReferenceCounted(true);
425 
426         // Create a separate wake lock for psds downloader as it may be released due to timeout.
427         mDownloadPsdsWakeLock = powerManager.newWakeLock(
428                 PowerManager.PARTIAL_WAKE_LOCK, "*location*:PsdsDownload");
429         mDownloadPsdsWakeLock.setReferenceCounted(true);
430 
431         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
432 
433         // App ops service to keep track of who is accessing the GPS
434         mAppOps = mContext.getSystemService(AppOpsManager.class);
435 
436         // Battery statistics service to be notified when GPS turns on or off
437         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
438                 BatteryStats.SERVICE_NAME));
439 
440         // Construct internal handler
441         mHandler = FgThread.getHandler();
442 
443         // Load GPS configuration and register listeners in the background:
444         // some operations, such as opening files and registering broadcast receivers, can take a
445         // relative long time, so the ctor() is kept to create objects needed by this instance,
446         // while IO initialization and registration is delegated to our internal handler
447         // this approach is just fine because events are posted to our handler anyway
448         mGnssConfiguration = mGnssNative.getConfiguration();
449         // Create a GPS net-initiated handler (also needed by handleInitialize)
450         GpsNetInitiatedHandler.EmergencyCallCallback emergencyCallCallback =
451                 new GpsNetInitiatedHandler.EmergencyCallCallback() {
452 
453                     @Override
454                     public void onEmergencyCallStart(int subId) {
455                         if (!mGnssConfiguration.isActiveSimEmergencySuplEnabled()) {
456                             return;
457                         }
458                         mHandler.post(() -> mGnssConfiguration.reloadGpsProperties(
459                                 mNIHandler.getInEmergency(), subId));
460                     }
461 
462                     @Override
463                     public void onEmergencyCallEnd() {
464                         if (!mGnssConfiguration.isActiveSimEmergencySuplEnabled()) {
465                             return;
466                         }
467                         mHandler.postDelayed(() -> mGnssConfiguration.reloadGpsProperties(
468                                         /* inEmergency= */ false,
469                                         SubscriptionManager.getDefaultDataSubscriptionId()),
470                                 TimeUnit.SECONDS.toMillis(mGnssConfiguration.getEsExtensionSec()));
471                     }
472                 };
473         mNIHandler = new GpsNetInitiatedHandler(context,
474                 emergencyCallCallback,
475                 mSuplEsEnabled);
476         // Trigger PSDS data download when the network comes up after booting.
477         mPendingDownloadPsdsTypes.add(GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX);
478         mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
479                 GnssLocationProvider.this::onNetworkAvailable,
480                 mHandler.getLooper(), mNIHandler);
481 
482         mNetworkTimeHelper = NetworkTimeHelper.create(mContext, mHandler.getLooper(), this);
483         mGnssSatelliteBlocklistHelper =
484                 new GnssSatelliteBlocklistHelper(mContext,
485                         mHandler.getLooper(), this);
486 
487         setAllowed(true);
488 
489         mGnssNative.addBaseCallbacks(this);
490         mGnssNative.addLocationCallbacks(this);
491         mGnssNative.addSvStatusCallbacks(this);
492         mGnssNative.setAGpsCallbacks(this);
493         mGnssNative.setPsdsCallbacks(this);
494         mGnssNative.setNotificationCallbacks(this);
495         mGnssNative.setLocationRequestCallbacks(this);
496         mGnssNative.setTimeCallbacks(this);
497     }
498 
499     /** Called when system is ready. */
onSystemReady()500     public synchronized void onSystemReady() {
501         mContext.registerReceiverAsUser(new BroadcastReceiver() {
502             @Override
503             public void onReceive(Context context, Intent intent) {
504                 if (getSendingUserId() == UserHandle.USER_ALL) {
505                     mShutdown = true;
506                     updateEnabled();
507                 }
508             }
509         }, UserHandle.ALL, new IntentFilter(Intent.ACTION_SHUTDOWN), null, mHandler);
510 
511         mContext.getContentResolver().registerContentObserver(
512                 Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
513                 true,
514                 new ContentObserver(mHandler) {
515                     @Override
516                     public void onChange(boolean selfChange) {
517                         updateEnabled();
518                     }
519                 }, UserHandle.USER_ALL);
520 
521         mHandler.post(this::handleInitialize);
522         mHandler.post(mGnssSatelliteBlocklistHelper::updateSatelliteBlocklist);
523     }
524 
handleInitialize()525     private void handleInitialize() {
526         if (mGnssNative.isGnssVisibilityControlSupported()) {
527             mGnssVisibilityControl = new GnssVisibilityControl(mContext, mHandler.getLooper(),
528                     mNIHandler);
529         }
530 
531         // load default GPS configuration
532         // (this configuration might change in the future based on SIM changes)
533         reloadGpsProperties();
534 
535         // listen for events
536         IntentFilter intentFilter = new IntentFilter();
537         intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
538         intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
539         mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler);
540 
541         if (!Flags.enableNiSuplMessageInjectionByCarrierConfigBugfix()) {
542             updateNiSuplMessageListenerRegistration(
543                     mGnssConfiguration.isNiSuplMessageInjectionEnabled());
544         }
545 
546         mNetworkConnectivityHandler.registerNetworkCallbacks();
547 
548         // permanently passively listen to all network locations
549         LocationManager locationManager = Objects.requireNonNull(
550                 mContext.getSystemService(LocationManager.class));
551         if (locationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
552             locationManager.requestLocationUpdates(
553                     LocationManager.NETWORK_PROVIDER,
554                     new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL)
555                             .setMinUpdateIntervalMillis(0)
556                             .setHiddenFromAppOps(true)
557                             .build(),
558                     DIRECT_EXECUTOR,
559                     this::injectLocation);
560         }
561 
562         updateEnabled();
563         synchronized (mLock) {
564             mInitialized = true;
565         }
566     }
567 
568     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
569         @Override
570         public void onReceive(Context context, Intent intent) {
571             String action = intent.getAction();
572             if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
573             if (action == null) {
574                 return;
575             }
576 
577             switch (action) {
578                 case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
579                 case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
580                     subscriptionOrCarrierConfigChanged();
581                     break;
582             }
583         }
584     };
585 
586     private BroadcastReceiver mNiSuplIntentReceiver = new BroadcastReceiver() {
587         @Override
588         public void onReceive(Context context, Intent intent) {
589             String action = intent.getAction();
590             if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
591             if (action == null) {
592                 return;
593             }
594 
595             switch (action) {
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                     // change period and/or lowPowerMode
1054                     if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
1055                             mFixInterval, mProviderRequest.isLowPower())) {
1056                         Log.e(TAG, "set_position_mode failed in updateRequirements");
1057                     }
1058                 } else if (!mStarted) {
1059                     // start GPS
1060                     startNavigating();
1061                 } else {
1062                     // GNSS Engine is already ON, but no GPS_CAPABILITY_SCHEDULING
1063                     mAlarmManager.cancel(mTimeoutListener);
1064                     if (mFixInterval >= NO_FIX_TIMEOUT) {
1065                         // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
1066                         // and our fix interval is not short
1067                         mAlarmManager.set(ELAPSED_REALTIME_WAKEUP,
1068                                 SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, TAG,
1069                                 mTimeoutListener, mHandler);
1070                     }
1071                 }
1072             }
1073         } else {
1074             updateClientUids(new WorkSource());
1075             stopNavigating();
1076             stopBatching();
1077         }
1078     }
1079 
setPositionMode(int mode, int recurrence, int minInterval, boolean lowPowerMode)1080     private boolean setPositionMode(int mode, int recurrence, int minInterval,
1081             boolean lowPowerMode) {
1082         GnssPositionMode positionMode = new GnssPositionMode(mode, recurrence, minInterval,
1083                 0, 0, lowPowerMode);
1084         if (mLastPositionMode != null && mLastPositionMode.equals(positionMode)) {
1085             return true;
1086         }
1087 
1088         boolean result = mGnssNative.setPositionMode(mode, recurrence, minInterval, 0, 0,
1089                 lowPowerMode);
1090         if (result) {
1091             mLastPositionMode = positionMode;
1092         } else {
1093             mLastPositionMode = null;
1094         }
1095         return result;
1096     }
1097 
updateClientUids(WorkSource source)1098     private void updateClientUids(WorkSource source) {
1099         if (source.equals(mClientSource)) {
1100             return;
1101         }
1102 
1103         // (1) Inform BatteryStats that the list of IDs we're tracking changed.
1104         try {
1105             mBatteryStats.noteGpsChanged(mClientSource, source);
1106         } catch (RemoteException e) {
1107             Log.w(TAG, "RemoteException", e);
1108         }
1109 
1110         // (2) Inform AppOps service about the list of changes to UIDs.
1111 
1112         // TODO: this doesn't seem correct, work chain attribution tag != package?
1113         List<WorkChain>[] diffs = WorkSource.diffChains(mClientSource, source);
1114         if (diffs != null) {
1115             List<WorkChain> newChains = diffs[0];
1116             List<WorkChain> goneChains = diffs[1];
1117 
1118             if (newChains != null) {
1119                 for (WorkChain newChain : newChains) {
1120                     mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
1121                             newChain.getAttributionTag());
1122                 }
1123             }
1124 
1125             if (goneChains != null) {
1126                 for (WorkChain goneChain : goneChains) {
1127                     mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
1128                             goneChain.getAttributionTag());
1129                 }
1130             }
1131 
1132             mClientSource.transferWorkChains(source);
1133         }
1134 
1135         // Update the flat UIDs and names list and inform app-ops of all changes.
1136         // TODO: why is GnssLocationProvider the only component using these deprecated APIs?
1137         WorkSource[] changes = mClientSource.setReturningDiffs(source);
1138         if (changes != null) {
1139             WorkSource newWork = changes[0];
1140             WorkSource goneWork = changes[1];
1141 
1142             // Update sources that were not previously tracked.
1143             if (newWork != null) {
1144                 for (int i = 0; i < newWork.size(); i++) {
1145                     mAppOps.startOpNoThrow(AppOpsManager.OP_GPS,
1146                             newWork.getUid(i), newWork.getPackageName(i));
1147                 }
1148             }
1149 
1150             // Update sources that are no longer tracked.
1151             if (goneWork != null) {
1152                 for (int i = 0; i < goneWork.size(); i++) {
1153                     mAppOps.finishOp(AppOpsManager.OP_GPS, goneWork.getUid(i),
1154                             goneWork.getPackageName(i));
1155                 }
1156             }
1157         }
1158     }
1159 
1160     @Override
onExtraCommand(int uid, int pid, String command, Bundle extras)1161     public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
1162         if ("delete_aiding_data".equals(command)) {
1163             deleteAidingData(extras);
1164         } else if ("force_time_injection".equals(command)) {
1165             demandUtcTimeInjection();
1166         } else if ("force_psds_injection".equals(command)) {
1167             if (mSupportsPsds) {
1168                 postWithWakeLockHeld(() -> handleDownloadPsdsData(
1169                         GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
1170             }
1171         } else if ("request_power_stats".equals(command)) {
1172             mGnssNative.requestPowerStats(Runnable::run, powerStats -> {});
1173         } else {
1174             Log.w(TAG, "sendExtraCommand: unknown command " + command);
1175         }
1176     }
1177 
deleteAidingData(Bundle extras)1178     private void deleteAidingData(Bundle extras) {
1179         int flags;
1180 
1181         if (extras == null) {
1182             flags = GNSS_AIDING_TYPE_ALL;
1183         } else {
1184             flags = 0;
1185             if (extras.getBoolean("ephemeris")) flags |= GNSS_AIDING_TYPE_EPHEMERIS;
1186             if (extras.getBoolean("almanac")) flags |= GNSS_AIDING_TYPE_ALMANAC;
1187             if (extras.getBoolean("position")) flags |= GNSS_AIDING_TYPE_POSITION;
1188             if (extras.getBoolean("time")) flags |= GNSS_AIDING_TYPE_TIME;
1189             if (extras.getBoolean("iono")) flags |= GNSS_AIDING_TYPE_IONO;
1190             if (extras.getBoolean("utc")) flags |= GNSS_AIDING_TYPE_UTC;
1191             if (extras.getBoolean("health")) flags |= GNSS_AIDING_TYPE_HEALTH;
1192             if (extras.getBoolean("svdir")) flags |= GNSS_AIDING_TYPE_SVDIR;
1193             if (extras.getBoolean("svsteer")) flags |= GNSS_AIDING_TYPE_SVSTEER;
1194             if (extras.getBoolean("sadata")) flags |= GNSS_AIDING_TYPE_SADATA;
1195             if (extras.getBoolean("rti")) flags |= GNSS_AIDING_TYPE_RTI;
1196             if (extras.getBoolean("celldb-info")) flags |= GNSS_AIDING_TYPE_CELLDB_INFO;
1197             if (extras.getBoolean("all")) flags |= GNSS_AIDING_TYPE_ALL;
1198         }
1199 
1200         if (flags != 0) {
1201             mGnssNative.deleteAidingData(flags);
1202         }
1203     }
1204 
startNavigating()1205     private void startNavigating() {
1206         if (!mStarted) {
1207             if (DEBUG) Log.d(TAG, "startNavigating");
1208             mTimeToFirstFix = 0;
1209             mLastFixTime = 0;
1210             setStarted(true);
1211             mPositionMode = GNSS_POSITION_MODE_STANDALONE;
1212 
1213             boolean agpsEnabled =
1214                     (Settings.Global.getInt(mContext.getContentResolver(),
1215                             Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
1216             mPositionMode = getSuplMode(agpsEnabled);
1217 
1218             if (DEBUG) {
1219                 String mode;
1220 
1221                 switch (mPositionMode) {
1222                     case GNSS_POSITION_MODE_STANDALONE:
1223                         mode = "standalone";
1224                         break;
1225                     case GNSS_POSITION_MODE_MS_ASSISTED:
1226                         mode = "MS_ASSISTED";
1227                         break;
1228                     case GNSS_POSITION_MODE_MS_BASED:
1229                         mode = "MS_BASED";
1230                         break;
1231                     default:
1232                         mode = "unknown";
1233                         break;
1234                 }
1235                 Log.d(TAG, "setting position_mode to " + mode);
1236             }
1237 
1238             int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
1239             if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
1240                     interval, mProviderRequest.isLowPower())) {
1241                 setStarted(false);
1242                 Log.e(TAG, "set_position_mode failed in startNavigating()");
1243                 return;
1244             }
1245             if (!mGnssNative.start()) {
1246                 setStarted(false);
1247                 Log.e(TAG, "native_start failed in startNavigating()");
1248                 return;
1249             }
1250 
1251             // reset SV count to zero
1252             mLocationExtras.reset();
1253             mFixRequestTime = SystemClock.elapsedRealtime();
1254             if (!mGnssNative.getCapabilities().hasScheduling()) {
1255                 // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
1256                 // and our fix interval is not short
1257                 if (mFixInterval >= NO_FIX_TIMEOUT) {
1258                     mAlarmManager.set(ELAPSED_REALTIME_WAKEUP,
1259                             SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, TAG, mTimeoutListener,
1260                             mHandler);
1261                 }
1262             }
1263         }
1264     }
1265 
stopNavigating()1266     private void stopNavigating() {
1267         if (DEBUG) Log.d(TAG, "stopNavigating");
1268         if (mStarted) {
1269             setStarted(false);
1270             mGnssNative.stop();
1271             mLastFixTime = 0;
1272             // native_stop() may reset the position mode in hardware.
1273             mLastPositionMode = null;
1274 
1275             // reset SV count to zero
1276             mLocationExtras.reset();
1277         }
1278         mAlarmManager.cancel(mTimeoutListener);
1279         mAlarmManager.cancel(mWakeupListener);
1280     }
1281 
startBatching(long batchLengthMs)1282     private void startBatching(long batchLengthMs) {
1283         long batchSize = batchLengthMs / mFixInterval;
1284 
1285         if (DEBUG) {
1286             Log.d(TAG, "startBatching " + mFixInterval + " " + batchLengthMs);
1287         }
1288         if (mGnssNative.startBatch(MILLISECONDS.toNanos(mFixInterval), 0, true)) {
1289             mBatchingStarted = true;
1290 
1291             if (batchSize < getBatchSize()) {
1292                 // if the batch size is smaller than the hardware batch size, use an alarm to flush
1293                 // locations as appropriate
1294                 mBatchingAlarm = () -> {
1295                     boolean flush = false;
1296                     synchronized (mLock) {
1297                         if (mBatchingAlarm != null) {
1298                             flush = true;
1299                             mAlarmManager.setExact(ELAPSED_REALTIME_WAKEUP,
1300                                     SystemClock.elapsedRealtime() + batchLengthMs, TAG,
1301                                     mBatchingAlarm, FgThread.getHandler());
1302                         }
1303                     }
1304 
1305                     if (flush) {
1306                         mGnssNative.flushBatch();
1307                     }
1308                 };
1309                 mAlarmManager.setExact(ELAPSED_REALTIME_WAKEUP,
1310                         SystemClock.elapsedRealtime() + batchLengthMs, TAG,
1311                         mBatchingAlarm, FgThread.getHandler());
1312             }
1313         } else {
1314             Log.e(TAG, "native_start_batch failed in startBatching()");
1315         }
1316     }
1317 
stopBatching()1318     private void stopBatching() {
1319         if (DEBUG) Log.d(TAG, "stopBatching");
1320         if (mBatchingStarted) {
1321             if (mBatchingAlarm != null) {
1322                 mAlarmManager.cancel(mBatchingAlarm);
1323                 mBatchingAlarm = null;
1324             }
1325             mGnssNative.flushBatch();
1326             mGnssNative.stopBatch();
1327             mBatchingStarted = false;
1328         }
1329     }
1330 
setStarted(boolean started)1331     private void setStarted(boolean started) {
1332         if (mStarted != started) {
1333             mStarted = started;
1334             mStartedChangedElapsedRealtime = SystemClock.elapsedRealtime();
1335         }
1336     }
1337 
hibernate()1338     private void hibernate() {
1339         // stop GPS until our next fix interval arrives
1340         stopNavigating();
1341         long now = SystemClock.elapsedRealtime();
1342         mAlarmManager.set(ELAPSED_REALTIME_WAKEUP, now + mFixInterval, TAG,
1343                 mWakeupListener, mHandler);
1344     }
1345 
handleReportLocation(boolean hasLatLong, Location location)1346     private void handleReportLocation(boolean hasLatLong, Location location) {
1347         if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
1348 
1349         location.setExtras(mLocationExtras.getBundle());
1350 
1351         try {
1352             reportLocation(LocationResult.wrap(location).validate());
1353         } catch (BadLocationException e) {
1354             Log.e(TAG, "Dropping invalid location: " + e);
1355             return;
1356         }
1357 
1358         if (mStarted) {
1359             mGnssMetrics.logReceivedLocationStatus(hasLatLong);
1360             if (hasLatLong) {
1361                 if (location.hasAccuracy()) {
1362                     mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
1363                 }
1364                 if (mTimeToFirstFix > 0) {
1365                     int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
1366                     mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
1367                 }
1368             }
1369         } else {
1370             // Warn or error about long delayed GNSS engine shutdown as this generally wastes
1371             // power and sends location when not expected.
1372             long locationAfterStartedFalseMillis =
1373                     SystemClock.elapsedRealtime() - mStartedChangedElapsedRealtime;
1374             if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS) {
1375                 String logMessage = "Unexpected GNSS Location report "
1376                         + TimeUtils.formatDuration(locationAfterStartedFalseMillis)
1377                         + " after location turned off";
1378                 if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS) {
1379                     Log.e(TAG, logMessage);
1380                 } else {
1381                     Log.w(TAG, logMessage);
1382                 }
1383             }
1384         }
1385 
1386         mLastFixTime = SystemClock.elapsedRealtime();
1387         // report time to first fix
1388         if (mTimeToFirstFix == 0 && hasLatLong) {
1389             mTimeToFirstFix = (int) (mLastFixTime - mFixRequestTime);
1390             if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
1391             if (mStarted) {
1392                 mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
1393             }
1394         }
1395 
1396         if (mStarted) {
1397             // For devices that use framework scheduling, a timer may be set to ensure we don't
1398             // spend too much power searching for a location, when the requested update rate is
1399             // slow.
1400             // As we just recievied a location, we'll cancel that timer.
1401             if (!mGnssNative.getCapabilities().hasScheduling() && mFixInterval < NO_FIX_TIMEOUT) {
1402                 mAlarmManager.cancel(mTimeoutListener);
1403             }
1404         }
1405 
1406         if (!mGnssNative.getCapabilities().hasScheduling() && mStarted
1407                 && mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
1408             if (DEBUG) Log.d(TAG, "got fix, hibernating");
1409             hibernate();
1410         }
1411     }
1412 
handleReportSvStatus(GnssStatus gnssStatus)1413     private void handleReportSvStatus(GnssStatus gnssStatus) {
1414         // Log CN0 as part of GNSS metrics
1415         mGnssMetrics.logCn0(gnssStatus);
1416 
1417         if (VERBOSE) {
1418             Log.v(TAG, "SV count: " + gnssStatus.getSatelliteCount());
1419         }
1420 
1421         Set<Pair<Integer, Integer>> satellites = new HashSet<>();
1422         int usedInFixCount = 0;
1423         int maxCn0 = 0;
1424         int meanCn0 = 0;
1425         for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) {
1426             if (gnssStatus.usedInFix(i)) {
1427                 satellites.add(
1428                         new Pair<>(gnssStatus.getConstellationType(i), gnssStatus.getSvid(i)));
1429                 ++usedInFixCount;
1430                 if (gnssStatus.getCn0DbHz(i) > maxCn0) {
1431                     maxCn0 = (int) gnssStatus.getCn0DbHz(i);
1432                 }
1433                 meanCn0 += gnssStatus.getCn0DbHz(i);
1434                 mGnssMetrics.logConstellationType(gnssStatus.getConstellationType(i));
1435             }
1436         }
1437         if (usedInFixCount > 0) {
1438             meanCn0 /= usedInFixCount;
1439         }
1440         // return number of sats used in fix instead of total reported
1441         mLocationExtras.set(satellites.size(), meanCn0, maxCn0);
1442 
1443         mGnssMetrics.logSvStatus(gnssStatus);
1444     }
1445 
updateNiSuplMessageListenerRegistration(boolean shouldRegister)1446     private void updateNiSuplMessageListenerRegistration(boolean shouldRegister) {
1447         if (!mNetworkConnectivityHandler.isNativeAgpsRilSupported()) {
1448             return;
1449         }
1450         if (mNiSuplMessageListenerRegistered == shouldRegister) {
1451             return;
1452         }
1453 
1454         // WAP PUSH NI SUPL message intent filter.
1455         // See User Plane Location Protocol Candidate Version 3.0,
1456         // OMA-TS-ULP-V3_0-20110920-C, Section 8.3 OMA Push.
1457         IntentFilter wapPushNiIntentFilter = new IntentFilter();
1458         wapPushNiIntentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
1459         try {
1460             wapPushNiIntentFilter
1461                 .addDataType("application/vnd.omaloc-supl-init");
1462         } catch (IntentFilter.MalformedMimeTypeException e) {
1463             Log.w(TAG, "Malformed SUPL init mime type");
1464         }
1465 
1466         // MT SMS NI SUPL message intent filter.
1467         // See User Plane Location Protocol Candidate Version 3.0,
1468         // OMA-TS-ULP-V3_0-20110920-C, Section 8.4 MT SMS.
1469         IntentFilter mtSmsNiIntentFilter = new IntentFilter();
1470         mtSmsNiIntentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
1471         mtSmsNiIntentFilter.addDataScheme("sms");
1472         mtSmsNiIntentFilter.addDataAuthority("localhost", "7275");
1473 
1474         if (shouldRegister) {
1475             mContext.registerReceiver(mNiSuplIntentReceiver,
1476                     wapPushNiIntentFilter, null, mHandler);
1477             mContext.registerReceiver(mNiSuplIntentReceiver,
1478                     mtSmsNiIntentFilter, null, mHandler);
1479             mNiSuplMessageListenerRegistered = true;
1480         } else {
1481             mContext.unregisterReceiver(mNiSuplIntentReceiver);
1482             mNiSuplMessageListenerRegistered = false;
1483         }
1484     }
1485 
restartLocationRequest()1486     private void restartLocationRequest() {
1487         if (DEBUG) Log.d(TAG, "restartLocationRequest");
1488         setStarted(false);
1489         updateRequirements();
1490     }
1491 
demandUtcTimeInjection()1492     private void demandUtcTimeInjection() {
1493         if (DEBUG) Log.d(TAG, "demandUtcTimeInjection");
1494         postWithWakeLockHeld(mNetworkTimeHelper::demandUtcTimeInjection);
1495     }
1496 
1497 
getCellType(CellInfo ci)1498     private static int getCellType(CellInfo ci) {
1499         if (ci instanceof CellInfoGsm) {
1500             return CellInfo.TYPE_GSM;
1501         } else if (ci instanceof CellInfoWcdma) {
1502             return CellInfo.TYPE_WCDMA;
1503         } else if (ci instanceof CellInfoLte) {
1504             return CellInfo.TYPE_LTE;
1505         } else if (ci instanceof CellInfoNr) {
1506             return CellInfo.TYPE_NR;
1507         }
1508         return CellInfo.TYPE_UNKNOWN;
1509     }
1510 
1511     /**
1512      * Extract the CID/CI for GSM/WCDMA/LTE/NR
1513      *
1514      * @return the cell ID or -1 if invalid
1515      */
getCidFromCellIdentity(CellIdentity id)1516     private static long getCidFromCellIdentity(CellIdentity id) {
1517         if (id == null) {
1518             return -1;
1519         }
1520         long cid = -1;
1521         switch(id.getType()) {
1522             case CellInfo.TYPE_GSM: cid = ((CellIdentityGsm) id).getCid(); break;
1523             case CellInfo.TYPE_WCDMA: cid = ((CellIdentityWcdma) id).getCid(); break;
1524             case CellInfo.TYPE_LTE: cid = ((CellIdentityLte) id).getCi(); break;
1525             case CellInfo.TYPE_NR: cid = ((CellIdentityNr) id).getNci(); break;
1526             default: break;
1527         }
1528         // If the CID is unreported
1529         if (cid == (id.getType() == CellInfo.TYPE_NR
1530                 ? CellInfo.UNAVAILABLE_LONG : CellInfo.UNAVAILABLE)) {
1531             cid = -1;
1532         }
1533 
1534         return cid;
1535     }
1536 
setRefLocation(int type, CellIdentity ci)1537     private void setRefLocation(int type, CellIdentity ci) {
1538         String mcc_str = ci.getMccString();
1539         String mnc_str = ci.getMncString();
1540         int mcc = mcc_str != null ? Integer.parseInt(mcc_str) : CellInfo.UNAVAILABLE;
1541         int mnc = mnc_str != null ? Integer.parseInt(mnc_str) : CellInfo.UNAVAILABLE;
1542         int lac = CellInfo.UNAVAILABLE;
1543         int tac = CellInfo.UNAVAILABLE;
1544         int pcid = CellInfo.UNAVAILABLE;
1545         int arfcn = CellInfo.UNAVAILABLE;
1546         long cid = CellInfo.UNAVAILABLE_LONG;
1547 
1548         switch (type) {
1549             case AGPS_REF_LOCATION_TYPE_GSM_CELLID:
1550                 CellIdentityGsm cig = (CellIdentityGsm) ci;
1551                 cid = cig.getCid();
1552                 lac = cig.getLac();
1553                 break;
1554             case AGPS_REF_LOCATION_TYPE_UMTS_CELLID:
1555                 CellIdentityWcdma ciw = (CellIdentityWcdma) ci;
1556                 cid = ciw.getCid();
1557                 lac = ciw.getLac();
1558                 break;
1559             case AGPS_REF_LOCATION_TYPE_LTE_CELLID:
1560                 CellIdentityLte cil = (CellIdentityLte) ci;
1561                 cid = cil.getCi();
1562                 tac = cil.getTac();
1563                 pcid = cil.getPci();
1564                 break;
1565             case AGPS_REF_LOCATION_TYPE_NR_CELLID:
1566                 CellIdentityNr cin = (CellIdentityNr) ci;
1567                 cid = cin.getNci();
1568                 tac = cin.getTac();
1569                 pcid = cin.getPci();
1570                 arfcn = cin.getNrarfcn();
1571                 break;
1572             default:
1573         }
1574 
1575         mGnssNative.setAgpsReferenceLocationCellId(
1576                 type, mcc, mnc, lac, cid, tac, pcid, arfcn);
1577     }
1578 
requestRefLocation()1579     private void requestRefLocation() {
1580         TelephonyManager phone = (TelephonyManager)
1581                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
1582 
1583         final int phoneType = phone.getPhoneType();
1584         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
1585 
1586             List<CellInfo> cil = phone.getAllCellInfo();
1587             if (cil != null) {
1588                 HashMap<Integer, CellIdentity> cellIdentityMap = new HashMap<>();
1589                 cil.sort(Comparator.comparingInt(
1590                         (CellInfo ci) -> ci.getCellSignalStrength().getAsuLevel()).reversed());
1591 
1592                 for (CellInfo ci : cil) {
1593                     int status = ci.getCellConnectionStatus();
1594                     if (ci.isRegistered()
1595                             || status == CellInfo.CONNECTION_PRIMARY_SERVING
1596                             || status == CellInfo.CONNECTION_SECONDARY_SERVING) {
1597                         CellIdentity c = ci.getCellIdentity();
1598                         int t = getCellType(ci);
1599                         if (getCidFromCellIdentity(c) != -1
1600                                 && !cellIdentityMap.containsKey(t)) {
1601                             cellIdentityMap.put(t, c);
1602                         }
1603                     }
1604                 }
1605 
1606                 if (cellIdentityMap.containsKey(CellInfo.TYPE_GSM)) {
1607                     setRefLocation(AGPS_REF_LOCATION_TYPE_GSM_CELLID,
1608                             cellIdentityMap.get(CellInfo.TYPE_GSM));
1609                 } else if (cellIdentityMap.containsKey(CellInfo.TYPE_WCDMA)) {
1610                     setRefLocation(AGPS_REF_LOCATION_TYPE_UMTS_CELLID,
1611                             cellIdentityMap.get(CellInfo.TYPE_WCDMA));
1612                 } else if (cellIdentityMap.containsKey(CellInfo.TYPE_LTE)) {
1613                     setRefLocation(AGPS_REF_LOCATION_TYPE_LTE_CELLID,
1614                             cellIdentityMap.get(CellInfo.TYPE_LTE));
1615                 } else if (cellIdentityMap.containsKey(CellInfo.TYPE_NR)) {
1616                     setRefLocation(AGPS_REF_LOCATION_TYPE_NR_CELLID,
1617                             cellIdentityMap.get(CellInfo.TYPE_NR));
1618                 } else {
1619                     Log.e(TAG, "No available serving cell information.");
1620                 }
1621             } else {
1622                 Log.e(TAG, "Error getting cell location info.");
1623             }
1624         } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
1625             Log.e(TAG, "CDMA not supported.");
1626         }
1627     }
1628 
postWithWakeLockHeld(Runnable runnable)1629     private void postWithWakeLockHeld(Runnable runnable) {
1630         // hold a wake lock until this message is delivered
1631         // note that this assumes the message will not be removed from the queue before
1632         // it is handled (otherwise the wake lock would be leaked).
1633         mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
1634         boolean success = mHandler.post(() -> {
1635             try {
1636                 runnable.run();
1637             } finally {
1638                 mWakeLock.release();
1639             }
1640         });
1641         if (!success) {
1642             mWakeLock.release();
1643         }
1644     }
1645 
1646     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1647     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1648         boolean dumpAll = false;
1649 
1650         int opti = 0;
1651         while (opti < args.length) {
1652             String opt = args[opti];
1653             if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
1654                 break;
1655             }
1656             opti++;
1657             if ("-a".equals(opt)) {
1658                 dumpAll = true;
1659                 break;
1660             }
1661         }
1662 
1663         pw.print("mStarted=" + mStarted + "   (changed ");
1664         TimeUtils.formatDuration(SystemClock.elapsedRealtime()
1665                 - mStartedChangedElapsedRealtime, pw);
1666         pw.println(" ago)");
1667         pw.println("mBatchingEnabled=" + mBatchingEnabled);
1668         pw.println("mBatchingStarted=" + mBatchingStarted);
1669         pw.println("mBatchSize=" + getBatchSize());
1670         pw.println("mFixInterval=" + mFixInterval);
1671         pw.print(mGnssMetrics.dumpGnssMetricsAsText());
1672         if (dumpAll) {
1673             mNetworkTimeHelper.dump(pw);
1674             pw.println("mSupportsPsds=" + mSupportsPsds);
1675             if (Flags.enableNiSuplMessageInjectionByCarrierConfigBugfix()) {
1676                 pw.println("mNiSuplMessageListenerRegistered="
1677                         + mNiSuplMessageListenerRegistered);
1678             }
1679             pw.println(
1680                     "PsdsServerConfigured=" + mGnssConfiguration.isLongTermPsdsServerConfigured());
1681             pw.println("native internal state: ");
1682             pw.println("  " + mGnssNative.getInternalState());
1683         }
1684     }
1685 
1686     @Override
onHalRestarted()1687     public void onHalRestarted() {
1688         reloadGpsProperties();
1689         if (isGpsEnabled()) {
1690             setGpsEnabled(false);
1691             updateEnabled();
1692             restartLocationRequest();
1693         }
1694 
1695         // Re-register network callbacks to get an update of available networks right away.
1696         synchronized (mLock) {
1697             if (mInitialized) {
1698                 mNetworkConnectivityHandler.unregisterNetworkCallbacks();
1699                 mNetworkConnectivityHandler.registerNetworkCallbacks();
1700             }
1701         }
1702     }
1703 
1704     @Override
onCapabilitiesChanged(GnssCapabilities oldCapabilities, GnssCapabilities newCapabilities)1705     public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
1706             GnssCapabilities newCapabilities) {
1707         mHandler.post(() -> {
1708             boolean useOnDemandTimeInjection = mGnssNative.getCapabilities().hasOnDemandTime();
1709 
1710             // b/73893222: There is a historic bug on Android, which means that the capability
1711             // "on demand time" is interpreted as "enable periodic injection" elsewhere but an
1712             // on-demand injection is done here. GNSS developers may have come to rely on the
1713             // periodic behavior, so it has been kept and all methods named to reflect what is
1714             // actually done. "On demand" requests are supported regardless of the capability.
1715             mNetworkTimeHelper.setPeriodicTimeInjectionMode(useOnDemandTimeInjection);
1716             if (useOnDemandTimeInjection) {
1717                 demandUtcTimeInjection();
1718             }
1719 
1720             restartLocationRequest();
1721         });
1722     }
1723 
1724     @Override
onReportLocation(boolean hasLatLong, Location location)1725     public void onReportLocation(boolean hasLatLong, Location location) {
1726         postWithWakeLockHeld(() -> handleReportLocation(hasLatLong, location));
1727     }
1728 
1729     @Override
onReportLocations(Location[] locations)1730     public void onReportLocations(Location[] locations) {
1731         if (DEBUG) {
1732             Log.d(TAG, "Location batch of size " + locations.length + " reported");
1733         }
1734 
1735         if (locations.length > 0) {
1736             // attempt to fix up timestamps if necessary
1737             if (locations.length > 1) {
1738                 // check any realtimes outside of expected bounds
1739                 boolean fixRealtime = false;
1740                 for (int i = locations.length - 2; i >= 0; i--) {
1741                     long timeDeltaMs = locations[i + 1].getTime() - locations[i].getTime();
1742                     long realtimeDeltaMs = locations[i + 1].getElapsedRealtimeMillis()
1743                             - locations[i].getElapsedRealtimeMillis();
1744                     if (abs(timeDeltaMs - realtimeDeltaMs) > MAX_BATCH_TIMESTAMP_DELTA_MS) {
1745                         fixRealtime = true;
1746                         break;
1747                     }
1748                 }
1749 
1750                 if (fixRealtime) {
1751                     // sort for monotonically increasing time before fixing realtime - realtime will
1752                     // thus also be monotonically increasing
1753                     Arrays.sort(locations,
1754                             Comparator.comparingLong(Location::getTime));
1755 
1756                     long expectedDeltaMs =
1757                             locations[locations.length - 1].getTime()
1758                                     - locations[locations.length - 1].getElapsedRealtimeMillis();
1759                     for (int i = locations.length - 2; i >= 0; i--) {
1760                         locations[i].setElapsedRealtimeNanos(
1761                                 MILLISECONDS.toNanos(
1762                                         max(locations[i].getTime() - expectedDeltaMs, 0)));
1763                     }
1764                 } else {
1765                     // sort for monotonically increasing realttime
1766                     Arrays.sort(locations,
1767                             Comparator.comparingLong(Location::getElapsedRealtimeNanos));
1768                 }
1769             }
1770 
1771             try {
1772                 reportLocation(LocationResult.wrap(locations).validate());
1773             } catch (BadLocationException e) {
1774                 Log.e(TAG, "Dropping invalid locations: " + e);
1775                 return;
1776             }
1777         }
1778 
1779         Runnable[] listeners;
1780         synchronized (mLock) {
1781             listeners = mFlushListeners.toArray(new Runnable[0]);
1782             mFlushListeners.clear();
1783         }
1784         for (Runnable listener : listeners) {
1785             listener.run();
1786         }
1787     }
1788 
1789     @Override
onReportSvStatus(GnssStatus gnssStatus)1790     public void onReportSvStatus(GnssStatus gnssStatus) {
1791         postWithWakeLockHeld(() -> handleReportSvStatus(gnssStatus));
1792     }
1793 
1794     @Override
onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr)1795     public void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
1796         mNetworkConnectivityHandler.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
1797     }
1798 
1799     @Override
onRequestPsdsDownload(int psdsType)1800     public void onRequestPsdsDownload(int psdsType) {
1801         postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
1802     }
1803 
1804     @Override
onRequestSetID(@nssNative.AGpsCallbacks.AgpsSetIdFlags int flags)1805     public void onRequestSetID(@GnssNative.AGpsCallbacks.AgpsSetIdFlags int flags) {
1806         TelephonyManager phone = (TelephonyManager)
1807                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
1808         int type = AGPS_SETID_TYPE_NONE;
1809         String setId = null;
1810 
1811         int subId = SubscriptionManager.getDefaultDataSubscriptionId();
1812         if (mGnssConfiguration.isActiveSimEmergencySuplEnabled() && mNIHandler.getInEmergency()
1813                 && mNetworkConnectivityHandler.getActiveSubId() >= 0) {
1814             subId = mNetworkConnectivityHandler.getActiveSubId();
1815         }
1816         if (SubscriptionManager.isValidSubscriptionId(subId)) {
1817             phone = phone.createForSubscriptionId(subId);
1818         }
1819         if ((flags & AGPS_REQUEST_SETID_IMSI) == AGPS_REQUEST_SETID_IMSI) {
1820             setId = phone.getSubscriberId();
1821             if (setId != null) {
1822                 // This means the framework has the SIM card.
1823                 type = AGPS_SETID_TYPE_IMSI;
1824             }
1825         } else if ((flags & AGPS_REQUEST_SETID_MSISDN) == AGPS_REQUEST_SETID_MSISDN) {
1826             setId = phone.getLine1Number();
1827             if (setId != null) {
1828                 // This means the framework has the SIM card.
1829                 type = AGPS_SETID_TYPE_MSISDN;
1830             }
1831         }
1832 
1833         mGnssNative.setAgpsSetId(type, (setId == null) ? "" : setId);
1834     }
1835 
1836     @Override
onRequestLocation(boolean independentFromGnss, boolean isUserEmergency)1837     public void onRequestLocation(boolean independentFromGnss, boolean isUserEmergency) {
1838         if (DEBUG) {
1839             Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss
1840                     + ", isUserEmergency: "
1841                     + isUserEmergency);
1842         }
1843         postWithWakeLockHeld(() -> handleRequestLocation(independentFromGnss, isUserEmergency));
1844     }
1845 
1846     @Override
onRequestUtcTime()1847     public void onRequestUtcTime() {
1848         demandUtcTimeInjection();
1849     }
1850 
1851     @Override
onRequestRefLocation()1852     public void onRequestRefLocation() {
1853         requestRefLocation();
1854     }
1855 
1856     @Override
onReportNfwNotification(String proxyAppPackageName, byte protocolStack, String otherProtocolStackName, byte requestor, String requestorId, byte responseType, boolean inEmergencyMode, boolean isCachedLocation)1857     public void onReportNfwNotification(String proxyAppPackageName, byte protocolStack,
1858             String otherProtocolStackName, byte requestor, String requestorId,
1859             byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
1860         if (mGnssVisibilityControl == null) {
1861             Log.e(TAG, "reportNfwNotification: mGnssVisibilityControl uninitialized.");
1862             return;
1863         }
1864 
1865         mGnssVisibilityControl.reportNfwNotification(proxyAppPackageName, protocolStack,
1866                 otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
1867                 isCachedLocation);
1868     }
1869 }
1870