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