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