1 /* 2 * Copyright (C) 2017 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 package com.android.statsd.loadtest; 17 18 import android.annotation.Nullable; 19 import android.app.Activity; 20 import android.app.AlarmManager; 21 import android.app.PendingIntent; 22 import android.app.StatsManager; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.graphics.Color; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.IStatsManager; 30 import android.os.PowerManager; 31 import android.os.PowerManager.WakeLock; 32 import android.os.ServiceManager; 33 import android.os.SystemClock; 34 import android.text.Editable; 35 import android.text.TextWatcher; 36 import android.util.Log; 37 import android.util.StatsLog; 38 import android.view.View; 39 import android.view.inputmethod.InputMethodManager; 40 import android.view.MotionEvent; 41 import android.view.View.OnFocusChangeListener; 42 import android.widget.AdapterView; 43 import android.widget.ArrayAdapter; 44 import android.widget.Button; 45 import android.widget.CheckBox; 46 import android.widget.EditText; 47 import android.widget.Spinner; 48 import android.widget.TextView; 49 import android.widget.Toast; 50 51 import com.android.os.StatsLog.ConfigMetricsReport; 52 import com.android.os.StatsLog.ConfigMetricsReportList; 53 import com.android.os.StatsLog.StatsdStatsReport; 54 import com.android.internal.os.StatsdConfigProto.TimeUnit; 55 56 import java.util.ArrayList; 57 import java.util.HashMap; 58 import java.util.List; 59 import java.util.Map; 60 61 /** 62 * Runs a load test for statsd. 63 * How it works: 64 * <ul> 65 * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths. 66 * <li> Periodically logs certain atoms into logd. 67 * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed 68 * in battery Historian. 69 * </ul> 70 * The load depends on how demanding the config is, as well as how frequently atoms are pushsed 71 * to logd. Those are all controlled by 4 adjustable parameters: 72 * <ul> 73 * <li> The 'replication' parameter artificially multiplies the number of metrics in the config. 74 * <li> The bucket size controls the time-bucketing the aggregate metrics. 75 * <li> The period parameter controls how frequently atoms are pushed to logd. 76 * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period). 77 * </ul> 78 */ 79 public class LoadtestActivity extends Activity implements AdapterView.OnItemSelectedListener { 80 81 private static final String TAG = "loadtest.LoadtestActivity"; 82 public static final String TYPE = "type"; 83 private static final String PUSH_ALARM = "push_alarm"; 84 public static final String PERF_ALARM = "perf_alarm"; 85 private static final String SET_REPLICATION = "set_replication"; 86 private static final String REPLICATION = "replication"; 87 private static final String START = "start"; 88 private static final String STOP = "stop"; 89 private static final Map<String, TimeUnit> TIME_UNIT_MAP = initializeTimeUnitMap(); 90 private static final List<String> TIME_UNIT_LABELS = initializeTimeUnitLabels(); 91 92 public final static class PusherAlarmReceiver extends BroadcastReceiver { 93 @Override onReceive(Context context, Intent intent)94 public void onReceive(Context context, Intent intent) { 95 Intent activityIntent = new Intent(context, LoadtestActivity.class); 96 activityIntent.putExtra(TYPE, PUSH_ALARM); 97 context.startActivity(activityIntent); 98 } 99 } 100 101 public final static class StopperAlarmReceiver extends BroadcastReceiver { 102 @Override onReceive(Context context, Intent intent)103 public void onReceive(Context context, Intent intent) { 104 Intent activityIntent = new Intent(context, LoadtestActivity.class); 105 activityIntent.putExtra(TYPE, STOP); 106 context.startActivity(activityIntent); 107 } 108 } 109 initializeTimeUnitMap()110 private static Map<String, TimeUnit> initializeTimeUnitMap() { 111 Map<String, TimeUnit> labels = new HashMap(); 112 labels.put("1m", TimeUnit.ONE_MINUTE); 113 labels.put("5m", TimeUnit.FIVE_MINUTES); 114 labels.put("10m", TimeUnit.TEN_MINUTES); 115 labels.put("30m", TimeUnit.THIRTY_MINUTES); 116 labels.put("1h", TimeUnit.ONE_HOUR); 117 labels.put("3h", TimeUnit.THREE_HOURS); 118 labels.put("6h", TimeUnit.SIX_HOURS); 119 labels.put("12h", TimeUnit.TWELVE_HOURS); 120 labels.put("1d", TimeUnit.ONE_DAY); 121 labels.put("1s", TimeUnit.CTS); 122 return labels; 123 } 124 initializeTimeUnitLabels()125 private static List<String> initializeTimeUnitLabels() { 126 List<String> labels = new ArrayList(); 127 labels.add("1s"); 128 labels.add("1m"); 129 labels.add("5m"); 130 labels.add("10m"); 131 labels.add("30m"); 132 labels.add("1h"); 133 labels.add("3h"); 134 labels.add("6h"); 135 labels.add("12h"); 136 labels.add("1d"); 137 return labels; 138 } 139 140 private AlarmManager mAlarmMgr; 141 142 /** 143 * Used to periodically log atoms to logd. 144 */ 145 private PendingIntent mPushPendingIntent; 146 147 /** 148 * Used to end the loadtest. 149 */ 150 private PendingIntent mStopPendingIntent; 151 152 private Button mStartStop; 153 private EditText mReplicationText; 154 private Spinner mBucketSpinner; 155 private EditText mPeriodText; 156 private EditText mBurstText; 157 private EditText mDurationText; 158 private TextView mReportText; 159 private CheckBox mPlaceboCheckBox; 160 private CheckBox mCountMetricCheckBox; 161 private CheckBox mDurationMetricCheckBox; 162 private CheckBox mEventMetricCheckBox; 163 private CheckBox mValueMetricCheckBox; 164 private CheckBox mGaugeMetricCheckBox; 165 166 /** 167 * When the load test started. 168 */ 169 private long mStartedTimeMillis; 170 171 /** 172 * For measuring perf data. 173 */ 174 private PerfData mPerfData; 175 176 /** 177 * For communicating with statsd. 178 */ 179 private StatsManager mStatsManager; 180 181 private PowerManager mPowerManager; 182 private WakeLock mWakeLock; 183 184 /** 185 * If true, we only measure the effect of the loadtest infrastructure. No atom are pushed and 186 * the configuration is empty. 187 */ 188 private boolean mPlacebo; 189 190 /** 191 * Whether to include CountMetric in the config. 192 */ 193 private boolean mIncludeCountMetric; 194 195 /** 196 * Whether to include DurationMetric in the config. 197 */ 198 private boolean mIncludeDurationMetric; 199 200 /** 201 * Whether to include EventMetric in the config. 202 */ 203 private boolean mIncludeEventMetric; 204 205 /** 206 * Whether to include ValueMetric in the config. 207 */ 208 private boolean mIncludeValueMetric; 209 210 /** 211 * Whether to include GaugeMetric in the config. 212 */ 213 private boolean mIncludeGaugeMetric; 214 215 /** 216 * The burst size. 217 */ 218 private int mBurst; 219 220 /** 221 * The metrics replication. 222 */ 223 private int mReplication; 224 225 /** 226 * The period, in seconds, at which batches of atoms are pushed. 227 */ 228 private long mPeriodSecs; 229 230 /** 231 * The bucket size, in minutes, for aggregate metrics. 232 */ 233 private TimeUnit mBucket; 234 235 /** 236 * The duration, in minutes, of the loadtest. 237 */ 238 private long mDurationMins; 239 240 /** 241 * Whether the loadtest has started. 242 */ 243 private boolean mStarted = false; 244 245 /** 246 * Orchestrates the logging of pushed events into logd. 247 */ 248 private SequencePusher mPusher; 249 250 /** 251 * Generates statsd configs. 252 */ 253 private ConfigFactory mFactory; 254 255 /** 256 * For intra-minute periods. 257 */ 258 private final Handler mHandler = new Handler(); 259 260 /** 261 * Number of metrics in the current config. 262 */ 263 private int mNumMetrics; 264 265 @Override onCreate(Bundle savedInstanceState)266 protected void onCreate(Bundle savedInstanceState) { 267 super.onCreate(savedInstanceState); 268 269 Log.d(TAG, "Starting loadtest Activity"); 270 271 setContentView(R.layout.activity_loadtest); 272 mReportText = (TextView) findViewById(R.id.report_text); 273 initBurst(); 274 initReplication(); 275 initBucket(); 276 initPeriod(); 277 initDuration(); 278 initPlacebo(); 279 initMetricWhitelist(); 280 281 // Hide the keyboard outside edit texts. 282 findViewById(R.id.outside).setOnTouchListener(new View.OnTouchListener() { 283 @Override 284 public boolean onTouch(View v, MotionEvent event) { 285 InputMethodManager imm = 286 (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 287 if (getCurrentFocus() != null) { 288 imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); 289 } 290 return true; 291 } 292 }); 293 294 mStartStop = findViewById(R.id.start_stop); 295 mStartStop.setOnClickListener(new View.OnClickListener() { 296 @Override 297 public void onClick(View view) { 298 if (mStarted) { 299 stopLoadtest(); 300 } else { 301 startLoadtest(); 302 } 303 } 304 }); 305 306 mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 307 mStatsManager = (StatsManager) getSystemService("stats"); 308 mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); 309 mFactory = new ConfigFactory(this); 310 stopLoadtest(); 311 mReportText.setText(""); 312 } 313 314 @Override onNewIntent(Intent intent)315 public void onNewIntent(Intent intent) { 316 String type = intent.getStringExtra(TYPE); 317 if (type == null) { 318 return; 319 } 320 switch (type) { 321 case PERF_ALARM: 322 onPerfAlarm(); 323 break; 324 case PUSH_ALARM: 325 onAlarm(); 326 break; 327 case SET_REPLICATION: 328 if (intent.hasExtra(REPLICATION)) { 329 setReplication(intent.getIntExtra(REPLICATION, 0)); 330 } 331 break; 332 case START: 333 startLoadtest(); 334 break; 335 case STOP: 336 stopLoadtest(); 337 break; 338 default: 339 throw new IllegalArgumentException("Unknown type: " + type); 340 } 341 } 342 343 @Override onDestroy()344 public void onDestroy() { 345 Log.d(TAG, "Destroying"); 346 mPerfData.onDestroy(); 347 stopLoadtest(); 348 clearConfigs(); 349 super.onDestroy(); 350 } 351 352 @Nullable getMetadata()353 public StatsdStatsReport getMetadata() { 354 if (!statsdRunning()) { 355 return null; 356 } 357 if (mStatsManager != null) { 358 byte[] data; 359 try { 360 data = mStatsManager.getStatsMetadata(); 361 } catch (StatsManager.StatsUnavailableException e) { 362 Log.e(TAG, "Failed to get data from statsd", e); 363 return null; 364 } 365 if (data != null) { 366 StatsdStatsReport report = null; 367 boolean good = false; 368 try { 369 return StatsdStatsReport.parseFrom(data); 370 } catch (com.google.protobuf.InvalidProtocolBufferException e) { 371 Log.d(TAG, "Bad StatsdStatsReport"); 372 } 373 } 374 } 375 return null; 376 } 377 378 @Nullable getData()379 public List<ConfigMetricsReport> getData() { 380 if (!statsdRunning()) { 381 return null; 382 } 383 if (mStatsManager != null) { 384 byte[] data; 385 try { 386 data = mStatsManager.getReports(ConfigFactory.CONFIG_ID); 387 } catch (StatsManager.StatsUnavailableException e) { 388 Log.e(TAG, "Failed to get data from statsd", e); 389 return null; 390 } 391 if (data != null) { 392 ConfigMetricsReportList reports = null; 393 try { 394 reports = ConfigMetricsReportList.parseFrom(data); 395 Log.d(TAG, "Num reports: " + reports.getReportsCount()); 396 StringBuilder sb = new StringBuilder(); 397 DisplayProtoUtils.displayLogReport(sb, reports); 398 Log.d(TAG, sb.toString()); 399 } catch (com.google.protobuf.InvalidProtocolBufferException e) { 400 Log.d(TAG, "Invalid data"); 401 } 402 if (reports != null) { 403 return reports.getReportsList(); 404 } 405 } 406 } 407 return null; 408 } 409 410 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)411 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 412 String item = parent.getItemAtPosition(position).toString(); 413 414 mBucket = TIME_UNIT_MAP.get(item); 415 } 416 417 @Override onNothingSelected(AdapterView<?> parent)418 public void onNothingSelected(AdapterView<?> parent) { 419 // Another interface callback 420 } 421 onPerfAlarm()422 private void onPerfAlarm() { 423 if (mPerfData != null) { 424 mPerfData.onAlarm(this); 425 } 426 // Piggy-back on that alarm to show the elapsed time. 427 long elapsedTimeMins = (long) Math.floor( 428 (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000); 429 mReportText.setText("Loadtest in progress.\n" 430 + "num metrics =" + mNumMetrics 431 + "\nElapsed time = " + elapsedTimeMins + " min(s)"); 432 } 433 onAlarm()434 private void onAlarm() { 435 Log.d(TAG, "ON ALARM"); 436 437 // Set the next task. 438 scheduleNext(); 439 440 // Do the work. 441 mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StatsdLoadTest"); 442 mWakeLock.acquire(); 443 if (mPusher != null) { 444 mPusher.next(); 445 } 446 mWakeLock.release(); 447 mWakeLock = null; 448 } 449 450 /** 451 * Schedules the next cycle of pushing atoms into logd. 452 */ scheduleNext()453 private void scheduleNext() { 454 Intent intent = new Intent(this, PusherAlarmReceiver.class); 455 intent.putExtra(TYPE, PUSH_ALARM); 456 mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); 457 long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000; 458 mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent); 459 } 460 startLoadtest()461 private synchronized void startLoadtest() { 462 if (mStarted) { 463 return; 464 } 465 466 // Clean up the state. 467 stopLoadtest(); 468 469 // Prepare to push a sequence of atoms to logd. 470 mPusher = new SequencePusher(mBurst, mPlacebo); 471 472 // Create a config and push it to statsd. 473 if (!setConfig(mFactory.getConfig(mReplication, mBucket, mPlacebo, 474 mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, 475 mIncludeValueMetric, mIncludeGaugeMetric))) { 476 return; 477 } 478 479 // Remember to stop in the future. 480 Intent intent = new Intent(this, StopperAlarmReceiver.class); 481 intent.putExtra(TYPE, STOP); 482 mStopPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); 483 long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000; 484 mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mStopPendingIntent); 485 486 // Log atoms. 487 scheduleNext(); 488 489 // Start tracking performance. 490 mPerfData = new PerfData(this, mPlacebo, mReplication, mBucket, mPeriodSecs, mBurst, 491 mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric, 492 mIncludeGaugeMetric); 493 mPerfData.startRecording(this); 494 495 mReportText.setText("Loadtest in progress.\nnum metrics =" + mNumMetrics); 496 mStartedTimeMillis = SystemClock.elapsedRealtime(); 497 498 updateStarted(true); 499 } 500 stopLoadtest()501 private synchronized void stopLoadtest() { 502 if (mPushPendingIntent != null) { 503 Log.d(TAG, "Canceling pre-existing push alarm"); 504 mAlarmMgr.cancel(mPushPendingIntent); 505 mPushPendingIntent = null; 506 } 507 if (mStopPendingIntent != null) { 508 Log.d(TAG, "Canceling pre-existing stop alarm"); 509 mAlarmMgr.cancel(mStopPendingIntent); 510 mStopPendingIntent = null; 511 } 512 if (mWakeLock != null) { 513 mWakeLock.release(); 514 mWakeLock = null; 515 } 516 if (mPerfData != null) { 517 mPerfData.stopRecording(this); 518 mPerfData.onDestroy(); 519 mPerfData = null; 520 } 521 522 // Obtain the latest data and display it. 523 getData(); 524 525 long elapsedTimeMins = (long) Math.floor( 526 (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000); 527 mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)"); 528 clearConfigs(); 529 updateStarted(false); 530 } 531 updateStarted(boolean started)532 private synchronized void updateStarted(boolean started) { 533 mStarted = started; 534 mStartStop.setBackgroundColor(started ? 535 Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00")); 536 mStartStop.setText(started ? getString(R.string.stop) : getString(R.string.start)); 537 updateControlsEnabled(); 538 } 539 updateControlsEnabled()540 private void updateControlsEnabled() { 541 mBurstText.setEnabled(!mPlacebo && !mStarted); 542 mReplicationText.setEnabled(!mPlacebo && !mStarted); 543 mPeriodText.setEnabled(!mStarted); 544 mBucketSpinner.setEnabled(!mPlacebo && !mStarted); 545 mDurationText.setEnabled(!mStarted); 546 mPlaceboCheckBox.setEnabled(!mStarted); 547 548 boolean enabled = !mStarted && !mPlaceboCheckBox.isChecked(); 549 mCountMetricCheckBox.setEnabled(enabled); 550 mDurationMetricCheckBox.setEnabled(enabled); 551 mEventMetricCheckBox.setEnabled(enabled); 552 mValueMetricCheckBox.setEnabled(enabled); 553 mGaugeMetricCheckBox.setEnabled(enabled); 554 } 555 statsdRunning()556 private boolean statsdRunning() { 557 if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) { 558 Log.d(TAG, "Statsd not running"); 559 Toast.makeText(LoadtestActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show(); 560 return false; 561 } 562 return true; 563 } 564 sanitizeInt(int val, int min, int max)565 private int sanitizeInt(int val, int min, int max) { 566 if (val > max) { 567 val = max; 568 } else if (val < min) { 569 val = min; 570 } 571 return val; 572 } 573 clearConfigs()574 private void clearConfigs() { 575 // TODO: Clear all configs instead of specific ones. 576 if (mStatsManager != null) { 577 if (mStarted) { 578 try { 579 mStatsManager.removeConfig(ConfigFactory.CONFIG_ID); 580 Log.d(TAG, "Removed loadtest statsd configs."); 581 } catch (StatsManager.StatsUnavailableException e) { 582 Log.e(TAG, "Failed to remove loadtest configs.", e); 583 } 584 } 585 } 586 } 587 setConfig(ConfigFactory.ConfigMetadata configData)588 private boolean setConfig(ConfigFactory.ConfigMetadata configData) { 589 if (mStatsManager != null) { 590 try { 591 mStatsManager.addConfig(ConfigFactory.CONFIG_ID, configData.bytes); 592 mNumMetrics = configData.numMetrics; 593 Log.d(TAG, "Config pushed to statsd"); 594 return true; 595 } catch (StatsManager.StatsUnavailableException | IllegalArgumentException e) { 596 Log.e(TAG, "Failed to push config to statsd", e); 597 } 598 } 599 return false; 600 } 601 setReplication(int replication)602 private synchronized void setReplication(int replication) { 603 if (mStarted) { 604 return; 605 } 606 mReplicationText.setText("" + replication); 607 } 608 setPeriodSecs(long periodSecs)609 private synchronized void setPeriodSecs(long periodSecs) { 610 mPeriodSecs = periodSecs; 611 } 612 setBurst(int burst)613 private synchronized void setBurst(int burst) { 614 mBurst = burst; 615 } 616 setDurationMins(long durationMins)617 private synchronized void setDurationMins(long durationMins) { 618 mDurationMins = durationMins; 619 } 620 621 handleFocus(EditText editText)622 private void handleFocus(EditText editText) { 623 /* 624 editText.setOnFocusChangeListener(new OnFocusChangeListener() { 625 @Override 626 public void onFocusChange(View v, boolean hasFocus) { 627 if (!hasFocus && editText.getText().toString().isEmpty()) { 628 editText.setText("-1", TextView.BufferType.EDITABLE); 629 } 630 } 631 }); 632 */ 633 } 634 initBurst()635 private void initBurst() { 636 mBurst = getResources().getInteger(R.integer.burst_default); 637 mBurstText = (EditText) findViewById(R.id.burst); 638 mBurstText.addTextChangedListener(new NumericalWatcher(mBurstText, 0, 1000) { 639 @Override 640 public void onNewValue(int newValue) { 641 setBurst(newValue); 642 } 643 }); 644 handleFocus(mBurstText); 645 } 646 initReplication()647 private void initReplication() { 648 mReplication = getResources().getInteger(R.integer.replication_default); 649 mReplicationText = (EditText) findViewById(R.id.replication); 650 mReplicationText.addTextChangedListener(new NumericalWatcher(mReplicationText, 1, 4096) { 651 @Override 652 public void onNewValue(int newValue) { 653 mReplication = newValue; 654 } 655 }); 656 handleFocus(mReplicationText); 657 } 658 initBucket()659 private void initBucket() { 660 String defaultValue = getResources().getString(R.string.bucket_default); 661 mBucket = TimeUnit.valueOf(defaultValue); 662 mBucketSpinner = (Spinner) findViewById(R.id.bucket_spinner); 663 664 ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>( 665 this, R.layout.spinner_item, TIME_UNIT_LABELS); 666 667 mBucketSpinner.setAdapter(dataAdapter); 668 mBucketSpinner.setOnItemSelectedListener(this); 669 670 for (String label : TIME_UNIT_MAP.keySet()) { 671 if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) { 672 mBucketSpinner.setSelection(dataAdapter.getPosition(label)); 673 } 674 } 675 } 676 initPeriod()677 private void initPeriod() { 678 mPeriodSecs = getResources().getInteger(R.integer.period_default); 679 mPeriodText = (EditText) findViewById(R.id.period); 680 mPeriodText.addTextChangedListener(new NumericalWatcher(mPeriodText, 1, 60) { 681 @Override 682 public void onNewValue(int newValue) { 683 setPeriodSecs(newValue); 684 } 685 }); 686 handleFocus(mPeriodText); 687 } 688 initDuration()689 private void initDuration() { 690 mDurationMins = getResources().getInteger(R.integer.duration_default); 691 mDurationText = (EditText) findViewById(R.id.duration); 692 mDurationText.addTextChangedListener(new NumericalWatcher(mDurationText, 1, 24 * 60) { 693 @Override 694 public void onNewValue(int newValue) { 695 setDurationMins(newValue); 696 } 697 }); 698 handleFocus(mDurationText); 699 } 700 initPlacebo()701 private void initPlacebo() { 702 mPlaceboCheckBox = findViewById(R.id.placebo); 703 mPlacebo = false; 704 mPlaceboCheckBox.setOnClickListener(new View.OnClickListener() { 705 @Override 706 public void onClick(View view) { 707 mPlacebo = mPlaceboCheckBox.isChecked(); 708 updateControlsEnabled(); 709 } 710 }); 711 } 712 initMetricWhitelist()713 private void initMetricWhitelist() { 714 mCountMetricCheckBox = findViewById(R.id.include_count); 715 mCountMetricCheckBox.setOnClickListener(new View.OnClickListener() { 716 @Override 717 public void onClick(View view) { 718 mIncludeCountMetric = mCountMetricCheckBox.isChecked(); 719 } 720 }); 721 mDurationMetricCheckBox = findViewById(R.id.include_duration); 722 mDurationMetricCheckBox.setOnClickListener(new View.OnClickListener() { 723 @Override 724 public void onClick(View view) { 725 mIncludeDurationMetric = mDurationMetricCheckBox.isChecked(); 726 } 727 }); 728 mEventMetricCheckBox = findViewById(R.id.include_event); 729 mEventMetricCheckBox.setOnClickListener(new View.OnClickListener() { 730 @Override 731 public void onClick(View view) { 732 mIncludeEventMetric = mEventMetricCheckBox.isChecked(); 733 } 734 }); 735 mValueMetricCheckBox = findViewById(R.id.include_value); 736 mValueMetricCheckBox.setOnClickListener(new View.OnClickListener() { 737 @Override 738 public void onClick(View view) { 739 mIncludeValueMetric = mValueMetricCheckBox.isChecked(); 740 } 741 }); 742 mGaugeMetricCheckBox = findViewById(R.id.include_gauge); 743 mGaugeMetricCheckBox.setOnClickListener(new View.OnClickListener() { 744 @Override 745 public void onClick(View view) { 746 mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked(); 747 } 748 }); 749 750 mIncludeCountMetric = mCountMetricCheckBox.isChecked(); 751 mIncludeDurationMetric = mDurationMetricCheckBox.isChecked(); 752 mIncludeEventMetric = mEventMetricCheckBox.isChecked(); 753 mIncludeValueMetric = mValueMetricCheckBox.isChecked(); 754 mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked(); 755 } 756 } 757