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