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