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