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 */
16package com.android.statsd.loadtest;
17
18import android.annotation.Nullable;
19import android.app.Activity;
20import android.app.AlarmManager;
21import android.app.PendingIntent;
22import android.app.StatsManager;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.graphics.Color;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.IStatsManager;
30import android.os.PowerManager;
31import android.os.PowerManager.WakeLock;
32import android.os.ServiceManager;
33import android.os.SystemClock;
34import android.text.Editable;
35import android.text.TextWatcher;
36import android.util.Log;
37import android.util.StatsLog;
38import android.view.View;
39import android.view.inputmethod.InputMethodManager;
40import android.view.MotionEvent;
41import android.view.View.OnFocusChangeListener;
42import android.widget.AdapterView;
43import android.widget.ArrayAdapter;
44import android.widget.Button;
45import android.widget.CheckBox;
46import android.widget.EditText;
47import android.widget.Spinner;
48import android.widget.TextView;
49import android.widget.Toast;
50
51import com.android.os.StatsLog.ConfigMetricsReport;
52import com.android.os.StatsLog.ConfigMetricsReportList;
53import com.android.os.StatsLog.StatsdStatsReport;
54import com.android.internal.os.StatsdConfigProto.TimeUnit;
55
56import java.util.ArrayList;
57import java.util.HashMap;
58import java.util.List;
59import 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 */
79public 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
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
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
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
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
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
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
344    public void onDestroy() {
345        Log.d(TAG, "Destroying");
346        mPerfData.onDestroy();
347        stopLoadtest();
348        clearConfigs();
349        super.onDestroy();
350    }
351
352    @Nullable
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
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
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
418    public void onNothingSelected(AdapterView<?> parent) {
419        // Another interface callback
420    }
421
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
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     */
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
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
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
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
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
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
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
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
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
602    private synchronized void setReplication(int replication) {
603        if (mStarted) {
604            return;
605        }
606        mReplicationText.setText("" + replication);
607    }
608
609    private synchronized void setPeriodSecs(long periodSecs) {
610        mPeriodSecs = periodSecs;
611    }
612
613    private synchronized void setBurst(int burst) {
614        mBurst = burst;
615    }
616
617    private synchronized void setDurationMins(long durationMins) {
618        mDurationMins = durationMins;
619    }
620
621
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
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
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
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
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
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
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
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