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