• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.healthconnect.logging;
18 
19 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED;
20 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__API_METHOD_UNKNOWN;
21 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__DELETE_DATA;
22 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__GET_CHANGES;
23 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__GET_CHANGES_TOKEN;
24 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__GET_GRANTED_PERMISSIONS;
25 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__INSERT_DATA;
26 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__READ_AGGREGATED_DATA;
27 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__READ_DATA;
28 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__REVOKE_ALL_PERMISSIONS;
29 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_METHOD__UPDATE_DATA;
30 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_STATUS__ERROR;
31 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_STATUS__STATUS_UNKNOWN;
32 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__API_STATUS__SUCCESS;
33 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__NOT_DEFINED;
34 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__NOT_USED;
35 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_ABOVE_3000;
36 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_BW_1000_TO_2000;
37 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_BW_2000_TO_3000;
38 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_BW_500_TO_1000;
39 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_UNDER_500;
40 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_ABOVE_5000;
41 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_1000_TO_2000;
42 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_2000_TO_3000;
43 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_3000_TO_4000;
44 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_4000_TO_5000;
45 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_UNDER_1000;
46 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_ABOVE_4000;
47 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_BW_1000_TO_2000;
48 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_BW_2000_TO_3000;
49 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_BW_3000_TO_4000;
50 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_UNDER_1000;
51 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_ABOVE_6000;
52 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_2000_TO_3000;
53 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_3000_TO_4000;
54 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_4000_TO_5000;
55 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_5000_TO_6000;
56 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_UNDER_2000;
57 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED;
58 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__ACTIVE_CALORIES_BURNED;
59 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BASAL_BODY_TEMPERATURE;
60 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BASAL_METABOLIC_RATE;
61 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BLOOD_GLUCOSE;
62 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BLOOD_PRESSURE;
63 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BODY_FAT;
64 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BODY_TEMPERATURE;
65 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BONE_MASS;
66 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__CERVICAL_MUCUS;
67 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__CYCLING_PEDALING_CADENCE;
68 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__DATA_TYPE_NOT_ASSIGNED;
69 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__DATA_TYPE_UNKNOWN;
70 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__DISTANCE;
71 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__ELEVATION_GAINED;
72 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__EXERCISE_SESSION;
73 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__FLOORS_CLIMBED;
74 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__HEART_RATE;
75 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__HEIGHT;
76 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__HYDRATION;
77 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__LEAN_BODY_MASS;
78 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__MENSTRUATION_FLOW;
79 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__NUTRITION;
80 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__OVULATION_TEST;
81 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__OXYGEN_SATURATION;
82 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__POWER;
83 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__RESPIRATORY_RATE;
84 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__RESTING_HEART_RATE;
85 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__SEXUAL_ACTIVITY;
86 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__SPEED;
87 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__STEPS;
88 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__STEPS_CADENCE;
89 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__TOTAL_CALORIES_BURNED;
90 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__VO2_MAX;
91 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__WEIGHT;
92 import static android.health.HealthFitnessStatsLog.HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__WHEELCHAIR_PUSHES;
93 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_ACTIVE_CALORIES_BURNED;
94 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BASAL_BODY_TEMPERATURE;
95 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BASAL_METABOLIC_RATE;
96 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BLOOD_GLUCOSE;
97 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BLOOD_PRESSURE;
98 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BODY_FAT;
99 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BODY_TEMPERATURE;
100 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BONE_MASS;
101 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_CERVICAL_MUCUS;
102 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_CYCLING_PEDALING_CADENCE;
103 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_DISTANCE;
104 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_ELEVATION_GAINED;
105 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_EXERCISE_SESSION;
106 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_FLOORS_CLIMBED;
107 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_HEART_RATE;
108 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_HEIGHT;
109 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_HYDRATION;
110 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_LEAN_BODY_MASS;
111 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_MENSTRUATION_FLOW;
112 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_NUTRITION;
113 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_OVULATION_TEST;
114 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_OXYGEN_SATURATION;
115 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_POWER;
116 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_RESPIRATORY_RATE;
117 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_RESTING_HEART_RATE;
118 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_SEXUAL_ACTIVITY;
119 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_SPEED;
120 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_STEPS;
121 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_STEPS_CADENCE;
122 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_TOTAL_CALORIES_BURNED;
123 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_UNKNOWN;
124 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_VO2_MAX;
125 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_WEIGHT;
126 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_WHEELCHAIR_PUSHES;
127 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_READS_PER_15M_BACKGROUND;
128 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_READS_PER_15M_FOREGROUND;
129 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_READS_PER_24H_BACKGROUND;
130 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_READS_PER_24H_FOREGROUND;
131 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_WRITES_PER_15M_BACKGROUND;
132 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_WRITES_PER_15M_FOREGROUND;
133 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_WRITES_PER_24H_BACKGROUND;
134 import static android.health.connect.ratelimiter.RateLimiter.QuotaBucket.QUOTA_BUCKET_WRITES_PER_24H_FOREGROUND;
135 
136 import android.annotation.IntDef;
137 import android.annotation.NonNull;
138 import android.health.HealthFitnessStatsLog;
139 import android.health.connect.internal.datatypes.RecordInternal;
140 import android.health.connect.ratelimiter.RateLimiter;
141 
142 import java.lang.annotation.Retention;
143 import java.lang.annotation.RetentionPolicy;
144 import java.util.ArrayList;
145 import java.util.Arrays;
146 import java.util.HashMap;
147 import java.util.HashSet;
148 import java.util.List;
149 import java.util.Map;
150 import java.util.Map.Entry;
151 import java.util.Objects;
152 
153 /**
154  * Class to log metrics from HealthConnectService
155  *
156  * @hide
157  */
158 public class HealthConnectServiceLogger {
159 
160     private final int mHealthDataServiceApiMethod;
161     private final int mHealthDataServiceApiStatus;
162     private final int mErrorCode;
163     private final long mDuration;
164     private final boolean mHoldsDataManagementPermission;
165     private final int mRateLimit;
166     private final int mNumberOfRecords;
167     private final int[] mRecordTypes;
168     private final String mPackageName;
169     private static final int MAX_NUMBER_OF_LOGGED_DATA_TYPES = 6;
170     private static final int RECORD_TYPE_NOT_ASSIGNED_DEFAULT_VALUE = -1;
171 
172     /**
173      * HealthConnectService ApiMethods supported by logging.
174      *
175      * @hide
176      */
177     public static final class ApiMethods {
178 
179         public static final int API_METHOD_UNKNOWN =
180                 HEALTH_CONNECT_API_CALLED__API_METHOD__API_METHOD_UNKNOWN;
181         public static final int DELETE_DATA = HEALTH_CONNECT_API_CALLED__API_METHOD__DELETE_DATA;
182         public static final int GET_CHANGES = HEALTH_CONNECT_API_CALLED__API_METHOD__GET_CHANGES;
183         public static final int GET_CHANGES_TOKEN =
184                 HEALTH_CONNECT_API_CALLED__API_METHOD__GET_CHANGES_TOKEN;
185         public static final int GET_GRANTED_PERMISSIONS =
186                 HEALTH_CONNECT_API_CALLED__API_METHOD__GET_GRANTED_PERMISSIONS;
187         public static final int INSERT_DATA = HEALTH_CONNECT_API_CALLED__API_METHOD__INSERT_DATA;
188         public static final int READ_AGGREGATED_DATA =
189                 HEALTH_CONNECT_API_CALLED__API_METHOD__READ_AGGREGATED_DATA;
190         public static final int READ_DATA = HEALTH_CONNECT_API_CALLED__API_METHOD__READ_DATA;
191         public static final int REVOKE_ALL_PERMISSIONS =
192                 HEALTH_CONNECT_API_CALLED__API_METHOD__REVOKE_ALL_PERMISSIONS;
193         public static final int UPDATE_DATA = HEALTH_CONNECT_API_CALLED__API_METHOD__UPDATE_DATA;
194 
195         @IntDef({
196             API_METHOD_UNKNOWN,
197             DELETE_DATA,
198             GET_CHANGES,
199             GET_CHANGES_TOKEN,
200             GET_GRANTED_PERMISSIONS,
201             INSERT_DATA,
202             READ_AGGREGATED_DATA,
203             READ_DATA,
204             REVOKE_ALL_PERMISSIONS,
205             UPDATE_DATA,
206         })
207         @Retention(RetentionPolicy.SOURCE)
208         public @interface ApiMethod {}
209     }
210 
211     /**
212      * Rate limiting ranges differentiated by Foreground/Background.
213      *
214      * @hide
215      */
216     public static final class RateLimitingRanges {
217 
218         public static final int NOT_USED = HEALTH_CONNECT_API_CALLED__RATE_LIMIT__NOT_USED;
219         public static final int NOT_DEFINED = HEALTH_CONNECT_API_CALLED__RATE_LIMIT__NOT_DEFINED;
220         public static final int FOREGROUND_15_MIN_UNDER_1000 =
221                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_UNDER_1000;
222         public static final int FOREGROUND_15_MIN_BW_1000_TO_2000 =
223                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_BW_1000_TO_2000;
224         public static final int FOREGROUND_15_MIN_BW_2000_TO_3000 =
225                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_BW_2000_TO_3000;
226         public static final int FOREGROUND_15_MIN_BW_3000_TO_4000 =
227                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_BW_3000_TO_4000;
228         public static final int FOREGROUND_15_MIN_ABOVE_4000 =
229                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_15_MIN_ABOVE_4000;
230         public static final int BACKGROUND_15_MIN_UNDER_500 =
231                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_UNDER_500;
232         public static final int BACKGROUND_15_MIN_BW_500_TO_1000 =
233                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_BW_500_TO_1000;
234         public static final int BACKGROUND_15_MIN_BW_1000_TO_2000 =
235                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_BW_1000_TO_2000;
236         public static final int BACKGROUND_15_MIN_BW_2000_TO_3000 =
237                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_BW_2000_TO_3000;
238         public static final int BACKGROUND_15_MIN_ABOVE_3000 =
239                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_15_MIN_ABOVE_3000;
240         public static final int FOREGROUND_24_HRS_UNDER_2000 =
241                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_UNDER_2000;
242         public static final int FOREGROUND_24_HRS_BW_2000_TO_3000 =
243                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_2000_TO_3000;
244         public static final int FOREGROUND_24_HRS_BW_3000_TO_4000 =
245                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_3000_TO_4000;
246         public static final int FOREGROUND_24_HRS_BW_4000_TO_5000 =
247                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_4000_TO_5000;
248         public static final int FOREGROUND_24_HRS_BW_5000_TO_6000 =
249                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_BW_5000_TO_6000;
250         public static final int FOREGROUND_24_HRS_ABOVE_6000 =
251                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_FOREGROUND_24_HRS_ABOVE_6000;
252         public static final int BACKGROUND_24_HRS_UNDER_1000 =
253                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_UNDER_1000;
254         public static final int BACKGROUND_24_HRS_BW_1000_TO_2000 =
255                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_1000_TO_2000;
256         public static final int BACKGROUND_24_HRS_BW_2000_TO_3000 =
257                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_2000_TO_3000;
258         public static final int BACKGROUND_24_HRS_BW_3000_TO_4000 =
259                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_3000_TO_4000;
260         public static final int BACKGROUND_24_HRS_BW_4000_TO_5000 =
261                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_BW_4000_TO_5000;
262         public static final int BACKGROUND_24_HRS_ABOVE_5000 =
263                 HEALTH_CONNECT_API_CALLED__RATE_LIMIT__RATE_LIMIT_BACKGROUND_24_HRS_ABOVE_5000;
264 
265         private static final Map<Integer, Integer> sForeground15min =
266                 Map.of(
267                         0,
268                         FOREGROUND_15_MIN_UNDER_1000,
269                         1,
270                         FOREGROUND_15_MIN_BW_1000_TO_2000,
271                         2,
272                         BACKGROUND_15_MIN_BW_2000_TO_3000,
273                         3,
274                         FOREGROUND_15_MIN_BW_3000_TO_4000);
275 
276         private static final Map<Integer, Integer> sForeground24hour =
277                 Map.of(
278                         0,
279                         FOREGROUND_24_HRS_UNDER_2000,
280                         1,
281                         FOREGROUND_24_HRS_UNDER_2000,
282                         2,
283                         FOREGROUND_24_HRS_BW_2000_TO_3000,
284                         3,
285                         FOREGROUND_24_HRS_BW_3000_TO_4000,
286                         4,
287                         FOREGROUND_24_HRS_BW_4000_TO_5000,
288                         5,
289                         FOREGROUND_24_HRS_BW_5000_TO_6000);
290 
291         private static final Map<Integer, Integer> sBackground15Min =
292                 Map.of(
293                         0,
294                         BACKGROUND_15_MIN_BW_500_TO_1000,
295                         1,
296                         BACKGROUND_15_MIN_BW_1000_TO_2000,
297                         2,
298                         BACKGROUND_15_MIN_BW_2000_TO_3000);
299 
300         private static final Map<Integer, Integer> sBackground24Hour =
301                 Map.of(
302                         0,
303                         BACKGROUND_24_HRS_UNDER_1000,
304                         1,
305                         BACKGROUND_24_HRS_BW_1000_TO_2000,
306                         2,
307                         BACKGROUND_24_HRS_BW_2000_TO_3000,
308                         3,
309                         BACKGROUND_24_HRS_BW_3000_TO_4000,
310                         4,
311                         BACKGROUND_24_HRS_BW_4000_TO_5000);
312 
313         @IntDef({
314             NOT_USED,
315             FOREGROUND_15_MIN_UNDER_1000,
316             FOREGROUND_15_MIN_BW_1000_TO_2000,
317             FOREGROUND_15_MIN_BW_2000_TO_3000,
318             FOREGROUND_15_MIN_BW_3000_TO_4000,
319             FOREGROUND_15_MIN_ABOVE_4000,
320             BACKGROUND_15_MIN_UNDER_500,
321             BACKGROUND_15_MIN_BW_500_TO_1000,
322             BACKGROUND_15_MIN_BW_1000_TO_2000,
323             BACKGROUND_15_MIN_BW_2000_TO_3000,
324             BACKGROUND_15_MIN_ABOVE_3000,
325             FOREGROUND_24_HRS_UNDER_2000,
326             FOREGROUND_24_HRS_BW_2000_TO_3000,
327             FOREGROUND_24_HRS_BW_3000_TO_4000,
328             FOREGROUND_24_HRS_BW_4000_TO_5000,
329             FOREGROUND_24_HRS_BW_5000_TO_6000,
330             FOREGROUND_24_HRS_ABOVE_6000,
331             BACKGROUND_24_HRS_UNDER_1000,
332             BACKGROUND_24_HRS_BW_1000_TO_2000,
333             BACKGROUND_24_HRS_BW_2000_TO_3000,
334             BACKGROUND_24_HRS_BW_3000_TO_4000,
335             BACKGROUND_24_HRS_BW_4000_TO_5000,
336             BACKGROUND_24_HRS_ABOVE_5000
337         })
338         @Retention(RetentionPolicy.SOURCE)
339         public @interface RateLimit {}
340     }
341 
342     /**
343      * Builder for HealthConnectServiceLogger
344      *
345      * @hide
346      */
347     public static class Builder {
348 
349         private final long mStartTime;
350         private final int mHealthDataServiceApiMethod;
351         private int mHealthDataServiceApiStatus;
352         private int mErrorCode;
353         private long mDuration;
354         private int mRateLimit;
355         private int mNumberOfRecords;
356         private final boolean mHoldsDataManagementPermission;
357         private int[] mRecordTypes;
358         private String mPackageName;
359 
Builder(boolean holdsDataManagementPermission, @ApiMethods.ApiMethod int apiMethod)360         public Builder(boolean holdsDataManagementPermission, @ApiMethods.ApiMethod int apiMethod) {
361             mStartTime = System.currentTimeMillis();
362             mHealthDataServiceApiMethod = apiMethod;
363             mHealthDataServiceApiStatus = HEALTH_CONNECT_API_CALLED__API_STATUS__STATUS_UNKNOWN;
364             mErrorCode = 0; // Means no error
365             mHoldsDataManagementPermission = holdsDataManagementPermission;
366             mRateLimit = RateLimitingRanges.NOT_USED;
367             mNumberOfRecords = 0;
368             mRecordTypes = new int[MAX_NUMBER_OF_LOGGED_DATA_TYPES];
369             Arrays.fill(mRecordTypes, RECORD_TYPE_NOT_ASSIGNED_DEFAULT_VALUE);
370             mPackageName = "UNKNOWN";
371         }
372 
373         /** Set the API was called successfully. */
setHealthDataServiceApiStatusSuccess()374         public Builder setHealthDataServiceApiStatusSuccess() {
375             this.mHealthDataServiceApiStatus = HEALTH_CONNECT_API_CALLED__API_STATUS__SUCCESS;
376             return this;
377         }
378 
379         /**
380          * Set the API threw error.
381          *
382          * @param errorCode Error code thrown by the API.
383          */
setHealthDataServiceApiStatusError(int errorCode)384         public Builder setHealthDataServiceApiStatusError(int errorCode) {
385             this.mErrorCode = errorCode;
386             this.mHealthDataServiceApiStatus = HEALTH_CONNECT_API_CALLED__API_STATUS__ERROR;
387             return this;
388         }
389 
390         /**
391          * Set the rate limiting range if used.
392          *
393          * @param quotaBucket Quota bucket.
394          * @param quotaLimit Bucket limit.
395          */
setRateLimit( @ateLimiter.QuotaBucket.Type int quotaBucket, float quotaLimit)396         public Builder setRateLimit(
397                 @RateLimiter.QuotaBucket.Type int quotaBucket, float quotaLimit) {
398             this.mRateLimit = calculateRateLimitEnum(quotaBucket, quotaLimit);
399             return this;
400         }
401 
402         /**
403          * Set the number of records involved in the API call.
404          *
405          * @param numberOfRecords Number of records.
406          */
setNumberOfRecords(int numberOfRecords)407         public Builder setNumberOfRecords(int numberOfRecords) {
408             this.mNumberOfRecords = numberOfRecords;
409             return this;
410         }
411 
412         /**
413          * Set the types of records.
414          *
415          * @param recordInternals List of records.
416          */
setDataTypesFromRecordInternals( @onNull List<RecordInternal<?>> recordInternals)417         public Builder setDataTypesFromRecordInternals(
418                 @NonNull List<RecordInternal<?>> recordInternals) {
419             Objects.requireNonNull(recordInternals);
420             Map<Integer, Integer> recordTypeToNumberOfRecords = new HashMap<>();
421             for (RecordInternal<?> recordInternal : recordInternals) {
422                 int recordType = getDataTypeEnumFromRecordType(recordInternal.getRecordType());
423                 int numberOfRecords = recordTypeToNumberOfRecords.getOrDefault(recordType, 0);
424                 numberOfRecords++;
425                 recordTypeToNumberOfRecords.put(recordType, numberOfRecords);
426             }
427             List<Entry<Integer, Integer>> recordTypeSortedByNumberOfRecords =
428                     new ArrayList<>(recordTypeToNumberOfRecords.entrySet());
429             recordTypeSortedByNumberOfRecords.sort(Entry.comparingByValue());
430             for (int i = 0;
431                     i
432                             < Math.min(
433                                     recordTypeSortedByNumberOfRecords.size(),
434                                     MAX_NUMBER_OF_LOGGED_DATA_TYPES);
435                     i++) {
436                 mRecordTypes[i] = recordTypeSortedByNumberOfRecords.get(i).getKey();
437             }
438             return this;
439         }
440 
441         /**
442          * Set the types of records.
443          *
444          * @param recordTypesList List of record types.
445          */
setDataTypesFromRecordTypes(@onNull List<Integer> recordTypesList)446         public Builder setDataTypesFromRecordTypes(@NonNull List<Integer> recordTypesList) {
447             if (recordTypesList == null || recordTypesList.size() == 0) {
448                 return this;
449             }
450             HashSet<Integer> recordTypes = new HashSet<>();
451             for (Integer recordType : recordTypesList) {
452                 recordTypes.add(getDataTypeEnumFromRecordType(recordType));
453             }
454 
455             int index = 0;
456             for (int recordType : recordTypes) {
457                 mRecordTypes[index++] = recordType;
458                 if (index == MAX_NUMBER_OF_LOGGED_DATA_TYPES) {
459                     break;
460                 }
461             }
462             return this;
463         }
464 
465         /**
466          * Set the types of records.
467          *
468          * @param packageName Package name of the caller.
469          */
setPackageName(@onNull String packageName)470         public Builder setPackageName(@NonNull String packageName) {
471             if (packageName == null || packageName.isBlank()) {
472                 return this;
473             }
474             mPackageName = packageName;
475             return this;
476         }
477 
478         /** Returns an object of {@link HealthConnectServiceLogger}. */
build()479         public HealthConnectServiceLogger build() {
480             mDuration = System.currentTimeMillis() - mStartTime;
481             return new HealthConnectServiceLogger(this);
482         }
483 
calculateRateLimitEnum( @ateLimiter.QuotaBucket.Type int quotaBucket, float quotaLimit)484         private int calculateRateLimitEnum(
485                 @RateLimiter.QuotaBucket.Type int quotaBucket, float quotaLimit) {
486             int quotient = (int) (quotaLimit / 1000);
487             switch (quotaBucket) {
488                 case QUOTA_BUCKET_READS_PER_15M_FOREGROUND:
489                 case QUOTA_BUCKET_WRITES_PER_15M_FOREGROUND:
490                     return RateLimitingRanges.sForeground15min.getOrDefault(
491                             quotient, RateLimitingRanges.FOREGROUND_15_MIN_ABOVE_4000);
492                 case QUOTA_BUCKET_READS_PER_24H_FOREGROUND:
493                 case QUOTA_BUCKET_WRITES_PER_24H_FOREGROUND:
494                     return RateLimitingRanges.sForeground24hour.getOrDefault(
495                             quotient, RateLimitingRanges.FOREGROUND_24_HRS_ABOVE_6000);
496                 case QUOTA_BUCKET_READS_PER_15M_BACKGROUND:
497                 case QUOTA_BUCKET_WRITES_PER_15M_BACKGROUND:
498                     if (quotaLimit < 500) {
499                         return RateLimitingRanges.BACKGROUND_15_MIN_UNDER_500;
500                     }
501                     return RateLimitingRanges.sBackground15Min.getOrDefault(
502                             quotient, RateLimitingRanges.BACKGROUND_15_MIN_ABOVE_3000);
503                 case QUOTA_BUCKET_READS_PER_24H_BACKGROUND:
504                 case QUOTA_BUCKET_WRITES_PER_24H_BACKGROUND:
505                     return RateLimitingRanges.sBackground24Hour.getOrDefault(
506                             quotient, RateLimitingRanges.BACKGROUND_24_HRS_ABOVE_5000);
507             }
508             return RateLimitingRanges.NOT_DEFINED;
509         }
510 
getDataTypeEnumFromRecordType(int recordType)511         private static int getDataTypeEnumFromRecordType(int recordType) {
512             switch (recordType) {
513                 case RECORD_TYPE_STEPS:
514                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__STEPS;
515                 case RECORD_TYPE_HEART_RATE:
516                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__HEART_RATE;
517                 case RECORD_TYPE_BASAL_METABOLIC_RATE:
518                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BASAL_METABOLIC_RATE;
519                 case RECORD_TYPE_CYCLING_PEDALING_CADENCE:
520                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__CYCLING_PEDALING_CADENCE;
521                 case RECORD_TYPE_POWER:
522                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__POWER;
523                 case RECORD_TYPE_SPEED:
524                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__SPEED;
525                 case RECORD_TYPE_STEPS_CADENCE:
526                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__STEPS_CADENCE;
527                 case RECORD_TYPE_DISTANCE:
528                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__DISTANCE;
529                 case RECORD_TYPE_WHEELCHAIR_PUSHES:
530                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__WHEELCHAIR_PUSHES;
531                 case RECORD_TYPE_TOTAL_CALORIES_BURNED:
532                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__TOTAL_CALORIES_BURNED;
533                 case RECORD_TYPE_FLOORS_CLIMBED:
534                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__FLOORS_CLIMBED;
535                 case RECORD_TYPE_ELEVATION_GAINED:
536                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__ELEVATION_GAINED;
537                 case RECORD_TYPE_ACTIVE_CALORIES_BURNED:
538                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__ACTIVE_CALORIES_BURNED;
539                 case RECORD_TYPE_HYDRATION:
540                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__HYDRATION;
541                 case RECORD_TYPE_NUTRITION:
542                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__NUTRITION;
543                 case RECORD_TYPE_RESPIRATORY_RATE:
544                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__RESPIRATORY_RATE;
545                 case RECORD_TYPE_BONE_MASS:
546                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BONE_MASS;
547                 case RECORD_TYPE_RESTING_HEART_RATE:
548                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__RESTING_HEART_RATE;
549                 case RECORD_TYPE_BODY_FAT:
550                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BODY_FAT;
551                 case RECORD_TYPE_VO2_MAX:
552                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__VO2_MAX;
553                 case RECORD_TYPE_CERVICAL_MUCUS:
554                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__CERVICAL_MUCUS;
555                 case RECORD_TYPE_BASAL_BODY_TEMPERATURE:
556                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BASAL_BODY_TEMPERATURE;
557                 case RECORD_TYPE_MENSTRUATION_FLOW:
558                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__MENSTRUATION_FLOW;
559                 case RECORD_TYPE_OXYGEN_SATURATION:
560                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__OXYGEN_SATURATION;
561                 case RECORD_TYPE_BLOOD_PRESSURE:
562                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BLOOD_PRESSURE;
563                 case RECORD_TYPE_HEIGHT:
564                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__HEIGHT;
565                 case RECORD_TYPE_BLOOD_GLUCOSE:
566                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BLOOD_GLUCOSE;
567                 case RECORD_TYPE_WEIGHT:
568                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__WEIGHT;
569                 case RECORD_TYPE_LEAN_BODY_MASS:
570                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__LEAN_BODY_MASS;
571                 case RECORD_TYPE_SEXUAL_ACTIVITY:
572                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__SEXUAL_ACTIVITY;
573                 case RECORD_TYPE_BODY_TEMPERATURE:
574                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__BODY_TEMPERATURE;
575                 case RECORD_TYPE_OVULATION_TEST:
576                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__OVULATION_TEST;
577                 case RECORD_TYPE_EXERCISE_SESSION:
578                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__EXERCISE_SESSION;
579                 case RECORD_TYPE_UNKNOWN:
580                 default:
581                     return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__DATA_TYPE_UNKNOWN;
582             }
583         }
584     }
585 
HealthConnectServiceLogger(@onNull HealthConnectServiceLogger.Builder builder)586     private HealthConnectServiceLogger(@NonNull HealthConnectServiceLogger.Builder builder) {
587         Objects.requireNonNull(builder);
588 
589         mHealthDataServiceApiMethod = builder.mHealthDataServiceApiMethod;
590         mHealthDataServiceApiStatus = builder.mHealthDataServiceApiStatus;
591         mErrorCode = builder.mErrorCode;
592         mDuration = builder.mDuration;
593         mHoldsDataManagementPermission = builder.mHoldsDataManagementPermission;
594         mRateLimit = builder.mRateLimit;
595         mNumberOfRecords = builder.mNumberOfRecords;
596         mRecordTypes = builder.mRecordTypes;
597         mPackageName = builder.mPackageName;
598     }
599 
600     /** Log to statsd. */
log()601     public void log() {
602 
603         // Do not log API calls made from the controller
604         if (mHoldsDataManagementPermission) {
605             return;
606         }
607         HealthFitnessStatsLog.write(
608                 HEALTH_CONNECT_API_CALLED,
609                 mHealthDataServiceApiMethod,
610                 mHealthDataServiceApiStatus,
611                 mErrorCode,
612                 mDuration,
613                 mNumberOfRecords,
614                 mRateLimit);
615 
616         // For private logging, max 6 data types per request are being logged
617         // rest will be ignored
618         HealthFitnessStatsLog.write(
619                 HEALTH_CONNECT_API_INVOKED,
620                 mHealthDataServiceApiMethod,
621                 mHealthDataServiceApiStatus,
622                 mErrorCode,
623                 mDuration,
624                 mPackageName,
625                 getRecordTypeEnumToLog(mRecordTypes, 0),
626                 getRecordTypeEnumToLog(mRecordTypes, 1),
627                 getRecordTypeEnumToLog(mRecordTypes, 2),
628                 getRecordTypeEnumToLog(mRecordTypes, 3),
629                 getRecordTypeEnumToLog(mRecordTypes, 4),
630                 getRecordTypeEnumToLog(mRecordTypes, 5));
631     }
632 
getRecordTypeEnumToLog(int[] recordTypes, int index)633     private int getRecordTypeEnumToLog(int[] recordTypes, int index) {
634         if (recordTypes[index] == RECORD_TYPE_NOT_ASSIGNED_DEFAULT_VALUE) {
635             return HEALTH_CONNECT_API_INVOKED__DATA_TYPE_ONE__DATA_TYPE_NOT_ASSIGNED;
636         }
637         return recordTypes[index];
638     }
639 }
640