• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.location.gnss;
18 
19 import 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 }