| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.physicalweb; | 5 package org.chromium.chrome.browser.physicalweb; |
| 6 | 6 |
| 7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.content.SharedPreferences; | 8 import android.content.SharedPreferences; |
| 9 import android.os.AsyncTask; | 9 import android.os.AsyncTask; |
| 10 import android.preference.PreferenceManager; | 10 import android.preference.PreferenceManager; |
| 11 | 11 |
| 12 import org.chromium.base.Log; | 12 import org.chromium.base.Log; |
| 13 import org.chromium.base.metrics.RecordHistogram; | 13 import org.chromium.base.metrics.RecordHistogram; |
| 14 import org.chromium.base.metrics.RecordUserAction; | 14 import org.chromium.base.metrics.RecordUserAction; |
| 15 | 15 |
| 16 import org.json.JSONArray; | 16 import org.json.JSONArray; |
| 17 import org.json.JSONException; | 17 import org.json.JSONException; |
| 18 | 18 |
| 19 import java.util.concurrent.TimeUnit; | 19 import java.util.concurrent.TimeUnit; |
| 20 | 20 |
| 21 import javax.annotation.concurrent.ThreadSafe; | 21 import javax.annotation.concurrent.ThreadSafe; |
| 22 | 22 |
| 23 /** | 23 /** |
| 24 * Centralizes UMA data collection for the Physical Web feature. | 24 * Centralizes UMA data collection for the Physical Web feature. |
| 25 */ | 25 */ |
| 26 @ThreadSafe | 26 @ThreadSafe |
| 27 public class PhysicalWebUma { | 27 public class PhysicalWebUma { |
| 28 private static final String TAG = "PhysicalWeb"; | 28 private static final String TAG = "PhysicalWeb"; |
| 29 private static final String HAS_DEFERRED_METRICS_KEY = "PhysicalWeb.HasDefer
redMetrics"; |
| 29 private static final String OPT_IN_DECLINE_BUTTON_PRESS_COUNT = | 30 private static final String OPT_IN_DECLINE_BUTTON_PRESS_COUNT = |
| 30 "PhysicalWeb.OptIn.DeclineButtonPressed"; | 31 "PhysicalWeb.OptIn.DeclineButtonPressed"; |
| 31 private static final String OPT_IN_ENABLE_BUTTON_PRESS_COUNT = | 32 private static final String OPT_IN_ENABLE_BUTTON_PRESS_COUNT = |
| 32 "PhysicalWeb.OptIn.EnableButtonPressed"; | 33 "PhysicalWeb.OptIn.EnableButtonPressed"; |
| 33 private static final String OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT = | 34 private static final String OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT = |
| 34 "PhysicalWeb.OptIn.HighPriorityNotificationShown"; | 35 "PhysicalWeb.OptIn.HighPriorityNotificationShown"; |
| 35 private static final String OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT = | 36 private static final String OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT = |
| 36 "PhysicalWeb.OptIn.MinPriorityNotificationShown"; | 37 "PhysicalWeb.OptIn.MinPriorityNotificationShown"; |
| 37 private static final String OPT_IN_NOTIFICATION_PRESS_COUNT = | 38 private static final String OPT_IN_NOTIFICATION_PRESS_COUNT = |
| 38 "PhysicalWeb.OptIn.NotificationPressed"; | 39 "PhysicalWeb.OptIn.NotificationPressed"; |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 198 /** | 199 /** |
| 199 * Uploads metrics that we have deferred for uploading. | 200 * Uploads metrics that we have deferred for uploading. |
| 200 * Additionally, this method will cause future stat records not to be deferr
ed and instead | 201 * Additionally, this method will cause future stat records not to be deferr
ed and instead |
| 201 * uploaded immediately. | 202 * uploaded immediately. |
| 202 */ | 203 */ |
| 203 public static void uploadDeferredMetrics(Context context) { | 204 public static void uploadDeferredMetrics(Context context) { |
| 204 // If uploads have been explicitely requested, they are now allowed. | 205 // If uploads have been explicitely requested, they are now allowed. |
| 205 sUploadAllowed = true; | 206 sUploadAllowed = true; |
| 206 | 207 |
| 207 // Read the metrics. | 208 // Read the metrics. |
| 208 UmaUploader uploader = new UmaUploader(); | |
| 209 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); | 209 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); |
| 210 uploader.urlSelectedCount = prefs.getInt(URL_SELECTED_COUNT, 0); | 210 if (prefs.getBoolean(HAS_DEFERRED_METRICS_KEY, false)) { |
| 211 uploader.optInDeclineButtonTapCount = prefs.getInt(OPT_IN_DECLINE_BUTTON
_PRESS_COUNT, 0); | 211 AsyncTask.THREAD_POOL_EXECUTOR.execute(new UmaUploader(prefs)); |
| 212 uploader.optInEnableButtonTapCount = prefs.getInt(OPT_IN_ENABLE_BUTTON_P
RESS_COUNT, 0); | |
| 213 uploader.optInHighPriorityNotificationCount = | |
| 214 prefs.getInt(OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT, 0); | |
| 215 uploader.optInMinPriorityNotificationCount = | |
| 216 prefs.getInt(OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT, 0); | |
| 217 uploader.optInNotificationPressCount = prefs.getInt(OPT_IN_NOTIFICATION_
PRESS_COUNT, 0); | |
| 218 uploader.prefsFeatureDisabledCount = prefs.getInt(PREFS_FEATURE_DISABLED
_COUNT, 0); | |
| 219 uploader.prefsFeatureEnabledCount = prefs.getInt(PREFS_FEATURE_ENABLED_C
OUNT, 0); | |
| 220 uploader.prefsLocationDeniedCount = prefs.getInt(PREFS_LOCATION_DENIED_C
OUNT, 0); | |
| 221 uploader.prefsLocationGrantedCount = prefs.getInt(PREFS_LOCATION_GRANTED
_COUNT, 0); | |
| 222 uploader.pwsBackgroundResolveTimes = prefs.getString(PWS_BACKGROUND_RESO
LVE_TIMES, "[]"); | |
| 223 uploader.pwsForegroundResolveTimes = prefs.getString(PWS_FOREGROUND_RESO
LVE_TIMES, "[]"); | |
| 224 uploader.standardNotificationPressDelays = | |
| 225 prefs.getString(STANDARD_NOTIFICATION_PRESS_DELAYS, "[]"); | |
| 226 uploader.optInNotificationPressDelays = | |
| 227 prefs.getString(OPT_IN_NOTIFICATION_PRESS_DELAYS, "[]"); | |
| 228 uploader.totalUrlsInitialCounts = prefs.getString(TOTAL_URLS_INITIAL_COU
NTS, "[]"); | |
| 229 uploader.totalUrlsRefreshCounts = prefs.getString(TOTAL_URLS_REFRESH_COU
NTS, "[]"); | |
| 230 uploader.activityReferrals = prefs.getString(ACTIVITY_REFERRALS, "[]"); | |
| 231 | |
| 232 // If the metrics are empty, we are done. | |
| 233 if (uploader.isEmpty()) { | |
| 234 return; | |
| 235 } | 212 } |
| 236 | |
| 237 // Clear out the stored deferred metrics that we are about to upload. | |
| 238 prefs.edit() | |
| 239 .remove(URL_SELECTED_COUNT) | |
| 240 .remove(OPT_IN_DECLINE_BUTTON_PRESS_COUNT) | |
| 241 .remove(OPT_IN_ENABLE_BUTTON_PRESS_COUNT) | |
| 242 .remove(OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT) | |
| 243 .remove(OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT) | |
| 244 .remove(OPT_IN_NOTIFICATION_PRESS_COUNT) | |
| 245 .remove(PREFS_FEATURE_DISABLED_COUNT) | |
| 246 .remove(PREFS_FEATURE_ENABLED_COUNT) | |
| 247 .remove(PREFS_LOCATION_DENIED_COUNT) | |
| 248 .remove(PREFS_LOCATION_GRANTED_COUNT) | |
| 249 .remove(PWS_BACKGROUND_RESOLVE_TIMES) | |
| 250 .remove(PWS_FOREGROUND_RESOLVE_TIMES) | |
| 251 .remove(STANDARD_NOTIFICATION_PRESS_DELAYS) | |
| 252 .remove(OPT_IN_NOTIFICATION_PRESS_DELAYS) | |
| 253 .remove(TOTAL_URLS_INITIAL_COUNTS) | |
| 254 .remove(TOTAL_URLS_REFRESH_COUNTS) | |
| 255 .remove(ACTIVITY_REFERRALS) | |
| 256 .apply(); | |
| 257 | |
| 258 // Finally, upload the metrics. | |
| 259 AsyncTask.THREAD_POOL_EXECUTOR.execute(uploader); | |
| 260 } | 213 } |
| 261 | 214 |
| 262 private static void storeAction(Context context, String key) { | 215 private static void storeAction(Context context, String key) { |
| 263 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); | 216 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); |
| 264 SharedPreferences.Editor prefsEditor = prefs.edit(); | |
| 265 int count = prefs.getInt(key, 0); | 217 int count = prefs.getInt(key, 0); |
| 266 prefsEditor.putInt(key, count + 1).apply(); | 218 prefs.edit() |
| 219 .putBoolean(HAS_DEFERRED_METRICS_KEY, true) |
| 220 .putInt(key, count + 1) |
| 221 .apply(); |
| 267 } | 222 } |
| 268 | 223 |
| 269 private static void storeValue(Context context, String key, Object value) { | 224 private static void storeValue(Context context, String key, Object value) { |
| 270 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); | 225 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); |
| 271 SharedPreferences.Editor prefsEditor = prefs.edit(); | 226 SharedPreferences.Editor prefsEditor = prefs.edit(); |
| 272 JSONArray values = null; | 227 JSONArray values = null; |
| 273 try { | 228 try { |
| 274 values = new JSONArray(prefs.getString(key, "[]")); | 229 values = new JSONArray(prefs.getString(key, "[]")); |
| 275 values.put(value); | 230 values.put(value); |
| 231 prefsEditor |
| 232 .putBoolean(HAS_DEFERRED_METRICS_KEY, true) |
| 233 .putString(key, values.toString()) |
| 234 .apply(); |
| 276 } catch (JSONException e) { | 235 } catch (JSONException e) { |
| 277 Log.e(TAG, "JSONException when storing " + key + " stats", e); | 236 Log.e(TAG, "JSONException when storing " + key + " stats", e); |
| 278 prefsEditor.remove(key).apply(); | 237 prefsEditor.remove(key).apply(); |
| 279 return; | 238 return; |
| 280 } | 239 } |
| 281 prefsEditor.putString(key, values.toString()).apply(); | 240 prefsEditor.putString(key, values.toString()).apply(); |
| 282 } | 241 } |
| 283 | 242 |
| 284 private static void handleAction(Context context, String key) { | 243 private static void handleAction(Context context, String key) { |
| 285 if (sUploadAllowed) { | 244 if (sUploadAllowed) { |
| 286 RecordUserAction.record(key); | 245 RecordUserAction.record(key); |
| 287 } else { | 246 } else { |
| 288 storeAction(context, key); | 247 storeAction(context, key); |
| 289 } | 248 } |
| 290 } | 249 } |
| 291 | 250 |
| 292 private static void handleTime(Context context, String key, long duration, T
imeUnit tu) { | 251 private static void handleTime(Context context, String key, long duration, T
imeUnit tu) { |
| 293 if (sUploadAllowed) { | 252 if (sUploadAllowed) { |
| 294 RecordHistogram.recordTimesHistogram(key, duration, tu); | 253 RecordHistogram.recordTimesHistogram(key, duration, tu); |
| 295 } else { | 254 } else { |
| 296 storeValue(context, key, duration); | 255 storeValue(context, key, duration); |
| 297 } | 256 } |
| 298 } | 257 } |
| 299 | 258 |
| 300 private static class UmaUploader implements Runnable { | 259 private static class UmaUploader implements Runnable { |
| 301 public int urlSelectedCount; | 260 SharedPreferences mPrefs; |
| 302 public int optInDeclineButtonTapCount; | |
| 303 public int optInEnableButtonTapCount; | |
| 304 public int optInHighPriorityNotificationCount; | |
| 305 public int optInMinPriorityNotificationCount; | |
| 306 public int optInNotificationPressCount; | |
| 307 public int prefsFeatureDisabledCount; | |
| 308 public int prefsFeatureEnabledCount; | |
| 309 public int prefsLocationDeniedCount; | |
| 310 public int prefsLocationGrantedCount; | |
| 311 public String pwsBackgroundResolveTimes; | |
| 312 public String pwsForegroundResolveTimes; | |
| 313 public String standardNotificationPressDelays; | |
| 314 public String optInNotificationPressDelays; | |
| 315 public String totalUrlsInitialCounts; | |
| 316 public String totalUrlsRefreshCounts; | |
| 317 public String activityReferrals; | |
| 318 | 261 |
| 319 public boolean isEmpty() { | 262 UmaUploader(SharedPreferences prefs) { |
| 320 return urlSelectedCount == 0 | 263 mPrefs = prefs; |
| 321 && optInDeclineButtonTapCount == 0 | |
| 322 && optInEnableButtonTapCount == 0 | |
| 323 && optInHighPriorityNotificationCount == 0 | |
| 324 && optInMinPriorityNotificationCount == 0 | |
| 325 && optInNotificationPressCount == 0 | |
| 326 && prefsFeatureDisabledCount == 0 | |
| 327 && prefsFeatureEnabledCount == 0 | |
| 328 && prefsLocationDeniedCount == 0 | |
| 329 && prefsLocationGrantedCount == 0 | |
| 330 && pwsBackgroundResolveTimes.equals("[]") | |
| 331 && pwsForegroundResolveTimes.equals("[]") | |
| 332 && standardNotificationPressDelays.equals("[]") | |
| 333 && optInNotificationPressDelays.equals("[]") | |
| 334 && totalUrlsInitialCounts.equals("[]") | |
| 335 && totalUrlsRefreshCounts.equals("[]") | |
| 336 && activityReferrals.equals("[]"); | |
| 337 } | |
| 338 | |
| 339 UmaUploader() { | |
| 340 } | 264 } |
| 341 | 265 |
| 342 @Override | 266 @Override |
| 343 public void run() { | 267 public void run() { |
| 344 uploadActions(urlSelectedCount, URL_SELECTED_COUNT); | 268 uploadActions(URL_SELECTED_COUNT); |
| 345 uploadActions(optInDeclineButtonTapCount, OPT_IN_DECLINE_BUTTON_PRES
S_COUNT); | 269 uploadActions(OPT_IN_DECLINE_BUTTON_PRESS_COUNT); |
| 346 uploadActions(optInEnableButtonTapCount, OPT_IN_ENABLE_BUTTON_PRESS_
COUNT); | 270 uploadActions(OPT_IN_ENABLE_BUTTON_PRESS_COUNT); |
| 347 uploadActions(optInHighPriorityNotificationCount, | 271 uploadActions(OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT); |
| 348 OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT); | 272 uploadActions(OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT); |
| 349 uploadActions(optInMinPriorityNotificationCount, | 273 uploadActions(OPT_IN_NOTIFICATION_PRESS_COUNT); |
| 350 OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT); | 274 uploadActions(PREFS_FEATURE_DISABLED_COUNT); |
| 351 uploadActions(optInNotificationPressCount, OPT_IN_NOTIFICATION_PRESS
_COUNT); | 275 uploadActions(PREFS_FEATURE_ENABLED_COUNT); |
| 352 uploadActions(prefsFeatureDisabledCount, PREFS_FEATURE_DISABLED_COUN
T); | 276 uploadActions(PREFS_LOCATION_DENIED_COUNT); |
| 353 uploadActions(prefsFeatureEnabledCount, PREFS_FEATURE_ENABLED_COUNT)
; | 277 uploadActions(PREFS_LOCATION_GRANTED_COUNT); |
| 354 uploadActions(prefsLocationDeniedCount, PREFS_LOCATION_DENIED_COUNT)
; | 278 uploadTimes(PWS_BACKGROUND_RESOLVE_TIMES, TimeUnit.MILLISECONDS); |
| 355 uploadActions(prefsLocationGrantedCount, PREFS_LOCATION_GRANTED_COUN
T); | 279 uploadTimes(PWS_FOREGROUND_RESOLVE_TIMES, TimeUnit.MILLISECONDS); |
| 356 uploadTimes(pwsBackgroundResolveTimes, PWS_BACKGROUND_RESOLVE_TIMES, | 280 uploadTimes(STANDARD_NOTIFICATION_PRESS_DELAYS, TimeUnit.MILLISECOND
S); |
| 357 TimeUnit.MILLISECONDS); | 281 uploadTimes(OPT_IN_NOTIFICATION_PRESS_DELAYS, TimeUnit.MILLISECONDS)
; |
| 358 uploadTimes(pwsForegroundResolveTimes, PWS_FOREGROUND_RESOLVE_TIMES, | 282 uploadCounts(TOTAL_URLS_INITIAL_COUNTS); |
| 359 TimeUnit.MILLISECONDS); | 283 uploadCounts(TOTAL_URLS_REFRESH_COUNTS); |
| 360 uploadTimes(standardNotificationPressDelays, STANDARD_NOTIFICATION_P
RESS_DELAYS, | 284 uploadEnums(ACTIVITY_REFERRALS, ListUrlsActivity.REFERER_BOUNDARY); |
| 361 TimeUnit.MILLISECONDS); | 285 removePref(HAS_DEFERRED_METRICS_KEY); |
| 362 uploadTimes(optInNotificationPressDelays, OPT_IN_NOTIFICATION_PRESS_
DELAYS, | |
| 363 TimeUnit.MILLISECONDS); | |
| 364 uploadCounts(totalUrlsInitialCounts, TOTAL_URLS_INITIAL_COUNTS); | |
| 365 uploadCounts(totalUrlsRefreshCounts, TOTAL_URLS_REFRESH_COUNTS); | |
| 366 uploadEnums(activityReferrals, ACTIVITY_REFERRALS, ListUrlsActivity.
REFERER_BOUNDARY); | |
| 367 } | 286 } |
| 368 | 287 |
| 369 private static void uploadActions(int count, String key) { | 288 private void removePref(String key) { |
| 370 for (int i = 0; i < count; i++) { | 289 mPrefs.edit() |
| 371 RecordUserAction.record(key); | 290 .remove(key) |
| 372 } | 291 .apply(); |
| 373 } | 292 } |
| 374 | 293 |
| 375 private static Number[] parseJsonNumberArray(String jsonArrayStr) { | 294 private static Number[] parseJsonNumberArray(String jsonArrayStr) { |
| 376 try { | 295 try { |
| 377 JSONArray values = new JSONArray(jsonArrayStr); | 296 JSONArray values = new JSONArray(jsonArrayStr); |
| 378 Number[] array = new Number[values.length()]; | 297 Number[] array = new Number[values.length()]; |
| 379 for (int i = 0; i < values.length(); i++) { | 298 for (int i = 0; i < values.length(); i++) { |
| 380 Object object = values.get(i); | 299 Object object = values.get(i); |
| 381 if (!(object instanceof Number)) { | 300 if (!(object instanceof Number)) { |
| 382 return null; | 301 return null; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 406 if (numbers == null) { | 325 if (numbers == null) { |
| 407 return null; | 326 return null; |
| 408 } | 327 } |
| 409 Integer[] array = new Integer[numbers.length]; | 328 Integer[] array = new Integer[numbers.length]; |
| 410 for (int i = 0; i < numbers.length; i++) { | 329 for (int i = 0; i < numbers.length; i++) { |
| 411 array[i] = numbers[i].intValue(); | 330 array[i] = numbers[i].intValue(); |
| 412 } | 331 } |
| 413 return array; | 332 return array; |
| 414 } | 333 } |
| 415 | 334 |
| 416 private static void uploadTimes(String jsonTimesStr, final String key, f
inal TimeUnit tu) { | 335 private void uploadActions(String key) { |
| 336 int count = mPrefs.getInt(key, 0); |
| 337 removePref(key); |
| 338 for (int i = 0; i < count; i++) { |
| 339 RecordUserAction.record(key); |
| 340 } |
| 341 } |
| 342 |
| 343 private void uploadTimes(final String key, final TimeUnit tu) { |
| 344 String jsonTimesStr = mPrefs.getString(key, "[]"); |
| 345 removePref(key); |
| 417 Long[] times = parseJsonLongArray(jsonTimesStr); | 346 Long[] times = parseJsonLongArray(jsonTimesStr); |
| 418 if (times == null) { | 347 if (times == null) { |
| 419 Log.e(TAG, "Error reporting " + key + " with values: " + jsonTim
esStr); | 348 Log.e(TAG, "Error reporting " + key + " with values: " + jsonTim
esStr); |
| 420 return; | 349 return; |
| 421 } | 350 } |
| 422 for (Long time : times) { | 351 for (Long time : times) { |
| 423 RecordHistogram.recordTimesHistogram(key, time, TimeUnit.MILLISE
CONDS); | 352 RecordHistogram.recordTimesHistogram(key, time, TimeUnit.MILLISE
CONDS); |
| 424 } | 353 } |
| 425 } | 354 } |
| 426 | 355 |
| 427 private static void uploadCounts(String jsonCountsStr, final String key)
{ | 356 private void uploadCounts(final String key) { |
| 357 String jsonCountsStr = mPrefs.getString(key, "[]"); |
| 358 removePref(key); |
| 428 Integer[] counts = parseJsonIntegerArray(jsonCountsStr); | 359 Integer[] counts = parseJsonIntegerArray(jsonCountsStr); |
| 429 if (counts == null) { | 360 if (counts == null) { |
| 430 Log.e(TAG, "Error reporting " + key + " with values: " + jsonCou
ntsStr); | 361 Log.e(TAG, "Error reporting " + key + " with values: " + jsonCou
ntsStr); |
| 431 return; | 362 return; |
| 432 } | 363 } |
| 433 for (Integer count: counts) { | 364 for (Integer count: counts) { |
| 434 RecordHistogram.recordCountHistogram(key, count); | 365 RecordHistogram.recordCountHistogram(key, count); |
| 435 } | 366 } |
| 436 } | 367 } |
| 437 | 368 |
| 438 private static void uploadEnums(String jsonEnumsStr, final String key, i
nt boundary) { | 369 private void uploadEnums(final String key, int boundary) { |
| 370 String jsonEnumsStr = mPrefs.getString(key, "[]"); |
| 371 removePref(key); |
| 439 Integer[] values = parseJsonIntegerArray(jsonEnumsStr); | 372 Integer[] values = parseJsonIntegerArray(jsonEnumsStr); |
| 440 if (values == null) { | 373 if (values == null) { |
| 441 Log.e(TAG, "Error reporting " + key + " with values: " + jsonEnu
msStr); | 374 Log.e(TAG, "Error reporting " + key + " with values: " + jsonEnu
msStr); |
| 442 return; | 375 return; |
| 443 } | 376 } |
| 444 for (Integer value: values) { | 377 for (Integer value: values) { |
| 445 RecordHistogram.recordEnumeratedHistogram(key, value, boundary); | 378 RecordHistogram.recordEnumeratedHistogram(key, value, boundary); |
| 446 } | 379 } |
| 447 } | 380 } |
| 448 } | 381 } |
| 449 } | 382 } |
| OLD | NEW |