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 android.app.StatsManager; 20 import android.content.Context; 21 import android.location.GnssSignalQuality; 22 import android.location.GnssStatus; 23 import android.os.RemoteException; 24 import android.os.SystemClock; 25 import android.os.SystemProperties; 26 import android.os.connectivity.GpsBatteryStats; 27 import android.text.format.DateUtils; 28 import android.util.Base64; 29 import android.util.Log; 30 import android.util.StatsEvent; 31 import android.util.TimeUtils; 32 33 import com.android.internal.app.IBatteryStats; 34 import com.android.internal.location.nano.GnssLogsProto.GnssLog; 35 import com.android.internal.location.nano.GnssLogsProto.PowerMetrics; 36 import com.android.internal.util.ConcurrentUtils; 37 import com.android.internal.util.FrameworkStatsLog; 38 import com.android.server.location.gnss.hal.GnssNative; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collections; 43 import java.util.List; 44 45 /** 46 * GnssMetrics: Is used for logging GNSS metrics 47 * 48 * @hide 49 */ 50 public class GnssMetrics { 51 52 private static final String TAG = "GnssMetrics"; 53 54 /** Default time between location fixes (in millisecs) */ 55 private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000; 56 private static final int CONVERT_MILLI_TO_MICRO = 1000; 57 private static final int VENDOR_SPECIFIC_POWER_MODES_SIZE = 10; 58 59 /** Frequency range of GPS L5, Galileo E5a, QZSS J5 frequency band */ 60 private static final double L5_CARRIER_FREQ_RANGE_LOW_HZ = 1164 * 1e6; 61 private static final double L5_CARRIER_FREQ_RANGE_HIGH_HZ = 1189 * 1e6; 62 63 64 private long mLogStartInElapsedRealtimeMs; 65 66 GnssPowerMetrics mGnssPowerMetrics; 67 68 // A boolean array indicating whether the constellation types have been used in fix. 69 private boolean[] mConstellationTypes; 70 private final Statistics mLocationFailureStatistics; 71 private final Statistics mTimeToFirstFixSecStatistics; 72 private final Statistics mPositionAccuracyMeterStatistics; 73 private final Statistics mTopFourAverageCn0Statistics; 74 private final Statistics mTopFourAverageCn0StatisticsL5; 75 // Total number of sv status messages processed 76 private int mNumSvStatus; 77 // Total number of L5 sv status messages processed 78 private int mNumL5SvStatus; 79 // Total number of sv status messages processed, where sv is used in fix 80 private int mNumSvStatusUsedInFix; 81 // Total number of L5 sv status messages processed, where sv is used in fix 82 private int mNumL5SvStatusUsedInFix; 83 84 Statistics mLocationFailureReportsStatistics; 85 Statistics mTimeToFirstFixMilliSReportsStatistics; 86 Statistics mPositionAccuracyMetersReportsStatistics; 87 Statistics mTopFourAverageCn0DbmHzReportsStatistics; 88 Statistics mL5TopFourAverageCn0DbmHzReportsStatistics; 89 long mSvStatusReports; 90 long mL5SvStatusReports; 91 long mSvStatusReportsUsedInFix; 92 long mL5SvStatusReportsUsedInFix; 93 94 private final StatsManager mStatsManager; 95 private final GnssNative mGnssNative; 96 GnssMetrics(Context context, IBatteryStats stats, GnssNative gnssNative)97 public GnssMetrics(Context context, IBatteryStats stats, GnssNative gnssNative) { 98 mGnssNative = gnssNative; 99 mGnssPowerMetrics = new GnssPowerMetrics(stats); 100 mLocationFailureStatistics = new Statistics(); 101 mTimeToFirstFixSecStatistics = new Statistics(); 102 mPositionAccuracyMeterStatistics = new Statistics(); 103 mTopFourAverageCn0Statistics = new Statistics(); 104 mTopFourAverageCn0StatisticsL5 = new Statistics(); 105 reset(); 106 mLocationFailureReportsStatistics = new Statistics(); 107 mTimeToFirstFixMilliSReportsStatistics = new Statistics(); 108 mPositionAccuracyMetersReportsStatistics = new Statistics(); 109 mTopFourAverageCn0DbmHzReportsStatistics = new Statistics(); 110 mL5TopFourAverageCn0DbmHzReportsStatistics = new Statistics(); 111 mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER); 112 registerGnssStats(); 113 } 114 115 /** 116 * Logs the status of a location report received from the HAL 117 */ logReceivedLocationStatus(boolean isSuccessful)118 public void logReceivedLocationStatus(boolean isSuccessful) { 119 if (!isSuccessful) { 120 mLocationFailureStatistics.addItem(1.0); 121 mLocationFailureReportsStatistics.addItem(1.0); 122 return; 123 } 124 mLocationFailureStatistics.addItem(0.0); 125 mLocationFailureReportsStatistics.addItem(0.0); 126 } 127 128 /** 129 * Logs missed reports 130 */ logMissedReports(int desiredTimeBetweenFixesMilliSeconds, int actualTimeBetweenFixesMilliSeconds)131 public void logMissedReports(int desiredTimeBetweenFixesMilliSeconds, 132 int actualTimeBetweenFixesMilliSeconds) { 133 int numReportMissed = (actualTimeBetweenFixesMilliSeconds / Math.max( 134 DEFAULT_TIME_BETWEEN_FIXES_MILLISECS, desiredTimeBetweenFixesMilliSeconds)) - 1; 135 if (numReportMissed > 0) { 136 for (int i = 0; i < numReportMissed; i++) { 137 mLocationFailureStatistics.addItem(1.0); 138 mLocationFailureReportsStatistics.addItem(1.0); 139 } 140 } 141 } 142 143 /** 144 * Logs time to first fix 145 */ logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds)146 public void logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds) { 147 mTimeToFirstFixSecStatistics.addItem(((double) timeToFirstFixMilliSeconds) / 1000); 148 mTimeToFirstFixMilliSReportsStatistics.addItem(timeToFirstFixMilliSeconds); 149 } 150 151 /** 152 * Logs position accuracy 153 */ logPositionAccuracyMeters(float positionAccuracyMeters)154 public void logPositionAccuracyMeters(float positionAccuracyMeters) { 155 mPositionAccuracyMeterStatistics.addItem(positionAccuracyMeters); 156 mPositionAccuracyMetersReportsStatistics.addItem(positionAccuracyMeters); 157 } 158 159 /** 160 * Logs CN0 when at least 4 SVs are available 161 */ logCn0(GnssStatus gnssStatus)162 public void logCn0(GnssStatus gnssStatus) { 163 logCn0L5(gnssStatus); 164 165 if (gnssStatus.getSatelliteCount() == 0) { 166 mGnssPowerMetrics.reportSignalQuality(null); 167 return; 168 } 169 170 float[] cn0DbHzs = new float[gnssStatus.getSatelliteCount()]; 171 for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) { 172 cn0DbHzs[i] = gnssStatus.getCn0DbHz(i); 173 } 174 175 Arrays.sort(cn0DbHzs); 176 mGnssPowerMetrics.reportSignalQuality(cn0DbHzs); 177 if (cn0DbHzs.length < 4) { 178 return; 179 } 180 if (cn0DbHzs[cn0DbHzs.length - 4] > 0.0) { 181 double top4AvgCn0 = 0.0; 182 for (int i = cn0DbHzs.length - 4; i < cn0DbHzs.length; i++) { 183 top4AvgCn0 += cn0DbHzs[i]; 184 } 185 top4AvgCn0 /= 4; 186 mTopFourAverageCn0Statistics.addItem(top4AvgCn0); 187 // Convert to mHz for accuracy 188 mTopFourAverageCn0DbmHzReportsStatistics.addItem(top4AvgCn0 * 1000); 189 } 190 } 191 isL5Sv(float carrierFreq)192 private static boolean isL5Sv(float carrierFreq) { 193 return (carrierFreq >= L5_CARRIER_FREQ_RANGE_LOW_HZ 194 && carrierFreq <= L5_CARRIER_FREQ_RANGE_HIGH_HZ); 195 } 196 197 /** 198 * Logs sv status data 199 */ logSvStatus(GnssStatus status)200 public void logSvStatus(GnssStatus status) { 201 boolean isL5; 202 // Calculate SvStatus Information 203 for (int i = 0; i < status.getSatelliteCount(); i++) { 204 if (status.hasCarrierFrequencyHz(i)) { 205 mNumSvStatus++; 206 mSvStatusReports++; 207 isL5 = isL5Sv(status.getCarrierFrequencyHz(i)); 208 if (isL5) { 209 mNumL5SvStatus++; 210 mL5SvStatusReports++; 211 } 212 if (status.usedInFix(i)) { 213 mNumSvStatusUsedInFix++; 214 mSvStatusReportsUsedInFix++; 215 if (isL5) { 216 mNumL5SvStatusUsedInFix++; 217 mL5SvStatusReportsUsedInFix++; 218 } 219 } 220 } 221 } 222 } 223 224 /** 225 * Logs CN0 when at least 4 SVs are available L5 Only 226 */ logCn0L5(GnssStatus gnssStatus)227 private void logCn0L5(GnssStatus gnssStatus) { 228 if (gnssStatus.getSatelliteCount() == 0) { 229 return; 230 } 231 // Create array list of all L5 satellites in report. 232 ArrayList<Float> l5Cn0DbHzs = new ArrayList<>(gnssStatus.getSatelliteCount()); 233 for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) { 234 if (isL5Sv(gnssStatus.getCarrierFrequencyHz(i))) { 235 l5Cn0DbHzs.add(gnssStatus.getCn0DbHz(i)); 236 } 237 } 238 if (l5Cn0DbHzs.size() < 4) { 239 return; 240 } 241 242 Collections.sort(l5Cn0DbHzs); 243 if (l5Cn0DbHzs.get(l5Cn0DbHzs.size() - 4) > 0.0) { 244 double top4AvgCn0 = 0.0; 245 for (int i = l5Cn0DbHzs.size() - 4; i < l5Cn0DbHzs.size(); i++) { 246 top4AvgCn0 += l5Cn0DbHzs.get(i); 247 } 248 top4AvgCn0 /= 4; 249 mTopFourAverageCn0StatisticsL5.addItem(top4AvgCn0); 250 // Convert to mHz for accuracy 251 mL5TopFourAverageCn0DbmHzReportsStatistics.addItem(top4AvgCn0 * 1000); 252 } 253 } 254 255 /** 256 * Logs that a constellation type has been observed. 257 */ logConstellationType(int constellationType)258 public void logConstellationType(int constellationType) { 259 if (constellationType >= mConstellationTypes.length) { 260 Log.e(TAG, "Constellation type " + constellationType + " is not valid."); 261 return; 262 } 263 mConstellationTypes[constellationType] = true; 264 } 265 266 /** 267 * Dumps GNSS metrics as a proto string 268 */ dumpGnssMetricsAsProtoString()269 public String dumpGnssMetricsAsProtoString() { 270 GnssLog msg = new GnssLog(); 271 if (mLocationFailureStatistics.getCount() > 0) { 272 msg.numLocationReportProcessed = mLocationFailureStatistics.getCount(); 273 msg.percentageLocationFailure = (int) (100.0 * mLocationFailureStatistics.getMean()); 274 } 275 if (mTimeToFirstFixSecStatistics.getCount() > 0) { 276 msg.numTimeToFirstFixProcessed = mTimeToFirstFixSecStatistics.getCount(); 277 msg.meanTimeToFirstFixSecs = (int) mTimeToFirstFixSecStatistics.getMean(); 278 msg.standardDeviationTimeToFirstFixSecs = 279 (int) mTimeToFirstFixSecStatistics.getStandardDeviation(); 280 } 281 if (mPositionAccuracyMeterStatistics.getCount() > 0) { 282 msg.numPositionAccuracyProcessed = mPositionAccuracyMeterStatistics.getCount(); 283 msg.meanPositionAccuracyMeters = (int) mPositionAccuracyMeterStatistics.getMean(); 284 msg.standardDeviationPositionAccuracyMeters = 285 (int) mPositionAccuracyMeterStatistics.getStandardDeviation(); 286 } 287 if (mTopFourAverageCn0Statistics.getCount() > 0) { 288 msg.numTopFourAverageCn0Processed = mTopFourAverageCn0Statistics.getCount(); 289 msg.meanTopFourAverageCn0DbHz = mTopFourAverageCn0Statistics.getMean(); 290 msg.standardDeviationTopFourAverageCn0DbHz = 291 mTopFourAverageCn0Statistics.getStandardDeviation(); 292 } 293 if (mNumSvStatus > 0) { 294 msg.numSvStatusProcessed = mNumSvStatus; 295 } 296 if (mNumL5SvStatus > 0) { 297 msg.numL5SvStatusProcessed = mNumL5SvStatus; 298 } 299 if (mNumSvStatusUsedInFix > 0) { 300 msg.numSvStatusUsedInFix = mNumSvStatusUsedInFix; 301 } 302 if (mNumL5SvStatusUsedInFix > 0) { 303 msg.numL5SvStatusUsedInFix = mNumL5SvStatusUsedInFix; 304 } 305 if (mTopFourAverageCn0StatisticsL5.getCount() > 0) { 306 msg.numL5TopFourAverageCn0Processed = mTopFourAverageCn0StatisticsL5.getCount(); 307 msg.meanL5TopFourAverageCn0DbHz = mTopFourAverageCn0StatisticsL5.getMean(); 308 msg.standardDeviationL5TopFourAverageCn0DbHz = 309 mTopFourAverageCn0StatisticsL5.getStandardDeviation(); 310 } 311 msg.powerMetrics = mGnssPowerMetrics.buildProto(); 312 msg.hardwareRevision = SystemProperties.get("ro.boot.revision", ""); 313 String s = Base64.encodeToString(GnssLog.toByteArray(msg), Base64.DEFAULT); 314 reset(); 315 return s; 316 } 317 318 /** 319 * Dumps GNSS Metrics as text 320 * 321 * @return GNSS Metrics 322 */ dumpGnssMetricsAsText()323 public String dumpGnssMetricsAsText() { 324 StringBuilder s = new StringBuilder(); 325 s.append("GNSS_KPI_START").append('\n'); 326 s.append(" KPI logging start time: "); 327 TimeUtils.formatDuration(mLogStartInElapsedRealtimeMs, s); 328 s.append("\n"); 329 s.append(" KPI logging end time: "); 330 TimeUtils.formatDuration(SystemClock.elapsedRealtime(), s); 331 s.append("\n"); 332 s.append(" Number of location reports: ").append( 333 mLocationFailureStatistics.getCount()).append("\n"); 334 if (mLocationFailureStatistics.getCount() > 0) { 335 s.append(" Percentage location failure: ").append( 336 100.0 * mLocationFailureStatistics.getMean()).append("\n"); 337 } 338 s.append(" Number of TTFF reports: ").append( 339 mTimeToFirstFixSecStatistics.getCount()).append("\n"); 340 if (mTimeToFirstFixSecStatistics.getCount() > 0) { 341 s.append(" TTFF mean (sec): ").append(mTimeToFirstFixSecStatistics.getMean()).append( 342 "\n"); 343 s.append(" TTFF standard deviation (sec): ").append( 344 mTimeToFirstFixSecStatistics.getStandardDeviation()).append("\n"); 345 } 346 s.append(" Number of position accuracy reports: ").append( 347 mPositionAccuracyMeterStatistics.getCount()).append("\n"); 348 if (mPositionAccuracyMeterStatistics.getCount() > 0) { 349 s.append(" Position accuracy mean (m): ").append( 350 mPositionAccuracyMeterStatistics.getMean()).append("\n"); 351 s.append(" Position accuracy standard deviation (m): ").append( 352 mPositionAccuracyMeterStatistics.getStandardDeviation()).append("\n"); 353 } 354 s.append(" Number of CN0 reports: ").append( 355 mTopFourAverageCn0Statistics.getCount()).append("\n"); 356 if (mTopFourAverageCn0Statistics.getCount() > 0) { 357 s.append(" Top 4 Avg CN0 mean (dB-Hz): ").append( 358 mTopFourAverageCn0Statistics.getMean()).append("\n"); 359 s.append(" Top 4 Avg CN0 standard deviation (dB-Hz): ").append( 360 mTopFourAverageCn0Statistics.getStandardDeviation()).append("\n"); 361 } 362 s.append(" Total number of sv status messages processed: ").append( 363 mNumSvStatus).append("\n"); 364 s.append(" Total number of L5 sv status messages processed: ").append( 365 mNumL5SvStatus).append("\n"); 366 s.append(" Total number of sv status messages processed, " 367 + "where sv is used in fix: ").append( 368 mNumSvStatusUsedInFix).append("\n"); 369 s.append(" Total number of L5 sv status messages processed, " 370 + "where sv is used in fix: ").append( 371 mNumL5SvStatusUsedInFix).append("\n"); 372 s.append(" Number of L5 CN0 reports: ").append( 373 mTopFourAverageCn0StatisticsL5.getCount()).append("\n"); 374 if (mTopFourAverageCn0StatisticsL5.getCount() > 0) { 375 s.append(" L5 Top 4 Avg CN0 mean (dB-Hz): ").append( 376 mTopFourAverageCn0StatisticsL5.getMean()).append("\n"); 377 s.append(" L5 Top 4 Avg CN0 standard deviation (dB-Hz): ").append( 378 mTopFourAverageCn0StatisticsL5.getStandardDeviation()).append("\n"); 379 } 380 s.append(" Used-in-fix constellation types: "); 381 for (int i = 0; i < mConstellationTypes.length; i++) { 382 if (mConstellationTypes[i]) { 383 s.append(GnssStatus.constellationTypeToString(i)).append(" "); 384 } 385 } 386 s.append("\n"); 387 s.append("GNSS_KPI_END").append("\n"); 388 GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats(); 389 if (stats != null) { 390 s.append("Power Metrics").append("\n"); 391 s.append(" Time on battery (min): ").append( 392 stats.getLoggingDurationMs() / ((double) DateUtils.MINUTE_IN_MILLIS)).append( 393 "\n"); 394 long[] t = stats.getTimeInGpsSignalQualityLevel(); 395 if (t != null && t.length == GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) { 396 s.append(" Amount of time (while on battery) Top 4 Avg CN0 > " 397 + GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ 398 + " dB-Hz (min): ").append( 399 t[1] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n"); 400 s.append(" Amount of time (while on battery) Top 4 Avg CN0 <= " 401 + GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ 402 + " dB-Hz (min): ").append( 403 t[0] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n"); 404 } 405 s.append(" Energy consumed while on battery (mAh): ").append( 406 stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS)).append( 407 "\n"); 408 } 409 s.append("Hardware Version: ").append(SystemProperties.get("ro.boot.revision", "")).append( 410 "\n"); 411 return s.toString(); 412 } 413 reset()414 private void reset() { 415 mLogStartInElapsedRealtimeMs = SystemClock.elapsedRealtime(); 416 mLocationFailureStatistics.reset(); 417 mTimeToFirstFixSecStatistics.reset(); 418 mPositionAccuracyMeterStatistics.reset(); 419 mTopFourAverageCn0Statistics.reset(); 420 resetConstellationTypes(); 421 mTopFourAverageCn0StatisticsL5.reset(); 422 mNumSvStatus = 0; 423 mNumL5SvStatus = 0; 424 mNumSvStatusUsedInFix = 0; 425 mNumL5SvStatusUsedInFix = 0; 426 } 427 428 /** Resets {@link #mConstellationTypes} as an all-false boolean array. */ resetConstellationTypes()429 public void resetConstellationTypes() { 430 mConstellationTypes = new boolean[GnssStatus.CONSTELLATION_COUNT]; 431 } 432 433 /** Thread-safe class for storing statistics */ 434 private static class Statistics { 435 436 private int mCount; 437 private double mSum; 438 private double mSumSquare; 439 private long mLongSum; 440 Statistics()441 Statistics() { 442 } 443 444 /** Resets statistics */ reset()445 public synchronized void reset() { 446 mCount = 0; 447 mSum = 0.0; 448 mSumSquare = 0.0; 449 mLongSum = 0; 450 } 451 452 /** Adds an item */ addItem(double item)453 public synchronized void addItem(double item) { 454 mCount++; 455 mSum += item; 456 mSumSquare += item * item; 457 mLongSum += item; 458 } 459 460 /** Returns number of items added */ getCount()461 public synchronized int getCount() { 462 return mCount; 463 } 464 465 /** Returns mean */ getMean()466 public synchronized double getMean() { 467 return mSum / mCount; 468 } 469 470 /** Returns standard deviation */ getStandardDeviation()471 public synchronized double getStandardDeviation() { 472 double m = mSum / mCount; 473 m = m * m; 474 double v = mSumSquare / mCount; 475 if (v > m) { 476 return Math.sqrt(v - m); 477 } 478 return 0; 479 } 480 481 /** Returns long sum */ getLongSum()482 public synchronized long getLongSum() { 483 return mLongSum; 484 } 485 } 486 487 /* Class for handling GNSS power related metrics */ 488 private class GnssPowerMetrics { 489 490 /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */ 491 public static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0; 492 493 /* Minimum change in Top Four Average CN0 needed to trigger a report */ 494 private static final double REPORTING_THRESHOLD_DB_HZ = 1.0; 495 496 /* BatteryStats API */ 497 private final IBatteryStats mBatteryStats; 498 499 /* Last reported Top Four Average CN0 */ 500 private double mLastAverageCn0; 501 502 /* Last reported signal quality bin (based on Top Four Average CN0) */ 503 private int mLastSignalLevel; 504 GnssPowerMetrics(IBatteryStats stats)505 GnssPowerMetrics(IBatteryStats stats) { 506 mBatteryStats = stats; 507 // Used to initialize the variable to a very small value (unachievable in practice) 508 // so that 509 // the first CNO report will trigger an update to BatteryStats 510 mLastAverageCn0 = -100.0; 511 mLastSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN; 512 } 513 514 /** 515 * Builds power metrics proto buf. This is included in the gnss proto buf. 516 * 517 * @return PowerMetrics 518 */ buildProto()519 public PowerMetrics buildProto() { 520 PowerMetrics p = new PowerMetrics(); 521 GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats(); 522 if (stats != null) { 523 p.loggingDurationMs = stats.getLoggingDurationMs(); 524 p.energyConsumedMah = 525 stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS); 526 long[] t = stats.getTimeInGpsSignalQualityLevel(); 527 p.timeInSignalQualityLevelMs = new long[t.length]; 528 System.arraycopy(t, 0, p.timeInSignalQualityLevelMs, 0, t.length); 529 } 530 return p; 531 } 532 533 /** 534 * Returns the GPS power stats 535 * 536 * @return GpsBatteryStats 537 */ getGpsBatteryStats()538 public GpsBatteryStats getGpsBatteryStats() { 539 try { 540 return mBatteryStats.getGpsBatteryStats(); 541 } catch (RemoteException e) { 542 Log.w(TAG, e); 543 return null; 544 } 545 } 546 547 /** 548 * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. 549 * If the number of SVs seen is less than 4, then signal quality is the average CN0. 550 * Changes are reported only if the average CN0 changes by more than 551 * REPORTING_THRESHOLD_DB_HZ. 552 */ reportSignalQuality(float[] sortedCn0DbHzs)553 public void reportSignalQuality(float[] sortedCn0DbHzs) { 554 double avgCn0 = 0.0; 555 if (sortedCn0DbHzs != null && sortedCn0DbHzs.length > 0) { 556 for (int i = Math.max(0, sortedCn0DbHzs.length - 4); i < sortedCn0DbHzs.length; 557 i++) { 558 avgCn0 += sortedCn0DbHzs[i]; 559 } 560 avgCn0 /= Math.min(sortedCn0DbHzs.length, 4); 561 } 562 if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) { 563 return; 564 } 565 int signalLevel = getSignalLevel(avgCn0); 566 if (signalLevel != mLastSignalLevel) { 567 FrameworkStatsLog.write(FrameworkStatsLog.GPS_SIGNAL_QUALITY_CHANGED, signalLevel); 568 mLastSignalLevel = signalLevel; 569 } 570 try { 571 mBatteryStats.noteGpsSignalQuality(signalLevel); 572 mLastAverageCn0 = avgCn0; 573 } catch (RemoteException e) { 574 Log.w(TAG, e); 575 } 576 } 577 578 /** 579 * Obtains signal level based on CN0 580 */ getSignalLevel(double cn0)581 private int getSignalLevel(double cn0) { 582 if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) { 583 return GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD; 584 } 585 return GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR; 586 } 587 } 588 registerGnssStats()589 private void registerGnssStats() { 590 StatsPullAtomCallbackImpl pullAtomCallback = new StatsPullAtomCallbackImpl(); 591 mStatsManager.setPullAtomCallback( 592 FrameworkStatsLog.GNSS_STATS, 593 null, // use default PullAtomMetadata values 594 ConcurrentUtils.DIRECT_EXECUTOR, pullAtomCallback); 595 mStatsManager.setPullAtomCallback( 596 FrameworkStatsLog.GNSS_POWER_STATS, 597 null, // use default PullAtomMetadata values 598 ConcurrentUtils.DIRECT_EXECUTOR, pullAtomCallback); 599 } 600 601 /** 602 * Stats Pull Atom Callback 603 * Calls the pull method to fill out gnss stats 604 */ 605 private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback { 606 StatsPullAtomCallbackImpl()607 StatsPullAtomCallbackImpl() { 608 } 609 610 @Override onPullAtom(int atomTag, List<StatsEvent> data)611 public int onPullAtom(int atomTag, List<StatsEvent> data) { 612 if (atomTag == FrameworkStatsLog.GNSS_STATS) { 613 data.add(FrameworkStatsLog.buildStatsEvent(atomTag, 614 mLocationFailureReportsStatistics.getCount(), 615 mLocationFailureReportsStatistics.getLongSum(), 616 mTimeToFirstFixMilliSReportsStatistics.getCount(), 617 mTimeToFirstFixMilliSReportsStatistics.getLongSum(), 618 mPositionAccuracyMetersReportsStatistics.getCount(), 619 mPositionAccuracyMetersReportsStatistics.getLongSum(), 620 mTopFourAverageCn0DbmHzReportsStatistics.getCount(), 621 mTopFourAverageCn0DbmHzReportsStatistics.getLongSum(), 622 mL5TopFourAverageCn0DbmHzReportsStatistics.getCount(), 623 mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum(), mSvStatusReports, 624 mSvStatusReportsUsedInFix, mL5SvStatusReports, 625 mL5SvStatusReportsUsedInFix)); 626 } else if (atomTag == FrameworkStatsLog.GNSS_POWER_STATS) { 627 mGnssNative.requestPowerStats(); 628 GnssPowerStats gnssPowerStats = mGnssNative.getPowerStats(); 629 if (gnssPowerStats == null) { 630 return StatsManager.PULL_SKIP; 631 } 632 double[] otherModesEnergyMilliJoule = new double[VENDOR_SPECIFIC_POWER_MODES_SIZE]; 633 double[] tempGnssPowerStatsOtherModes = 634 gnssPowerStats.getOtherModesEnergyMilliJoule(); 635 if (tempGnssPowerStatsOtherModes.length < VENDOR_SPECIFIC_POWER_MODES_SIZE) { 636 System.arraycopy(tempGnssPowerStatsOtherModes, 0, 637 otherModesEnergyMilliJoule, 0, 638 tempGnssPowerStatsOtherModes.length); 639 } else { 640 System.arraycopy(tempGnssPowerStatsOtherModes, 0, 641 otherModesEnergyMilliJoule, 0, 642 VENDOR_SPECIFIC_POWER_MODES_SIZE); 643 } 644 data.add(FrameworkStatsLog.buildStatsEvent(atomTag, 645 (long) (gnssPowerStats.getElapsedRealtimeUncertaintyNanos()), 646 (long) (gnssPowerStats.getTotalEnergyMilliJoule() * CONVERT_MILLI_TO_MICRO), 647 (long) (gnssPowerStats.getSinglebandTrackingModeEnergyMilliJoule() 648 * CONVERT_MILLI_TO_MICRO), 649 (long) (gnssPowerStats.getMultibandTrackingModeEnergyMilliJoule() 650 * CONVERT_MILLI_TO_MICRO), 651 (long) (gnssPowerStats.getSinglebandAcquisitionModeEnergyMilliJoule() 652 * CONVERT_MILLI_TO_MICRO), 653 (long) (gnssPowerStats.getMultibandAcquisitionModeEnergyMilliJoule() 654 * CONVERT_MILLI_TO_MICRO), 655 (long) (otherModesEnergyMilliJoule[0] * CONVERT_MILLI_TO_MICRO), 656 (long) (otherModesEnergyMilliJoule[1] * CONVERT_MILLI_TO_MICRO), 657 (long) (otherModesEnergyMilliJoule[2] * CONVERT_MILLI_TO_MICRO), 658 (long) (otherModesEnergyMilliJoule[3] * CONVERT_MILLI_TO_MICRO), 659 (long) (otherModesEnergyMilliJoule[4] * CONVERT_MILLI_TO_MICRO), 660 (long) (otherModesEnergyMilliJoule[5] * CONVERT_MILLI_TO_MICRO), 661 (long) (otherModesEnergyMilliJoule[6] * CONVERT_MILLI_TO_MICRO), 662 (long) (otherModesEnergyMilliJoule[7] * CONVERT_MILLI_TO_MICRO), 663 (long) (otherModesEnergyMilliJoule[8] * CONVERT_MILLI_TO_MICRO), 664 (long) (otherModesEnergyMilliJoule[9] * CONVERT_MILLI_TO_MICRO))); 665 } else { 666 throw new UnsupportedOperationException("Unknown tagId = " + atomTag); 667 } 668 return StatsManager.PULL_SUCCESS; 669 } 670 } 671 }