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