blob: 4b294fefa3af11c1fa656a27a742db50094975a6 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.tradefed.device.metric;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.DurationBucketInfo;
import com.android.os.StatsLog.DurationMetricData;
import com.android.os.StatsLog.StatsLogReport.DurationMetricDataWrapper;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.util.Sl4aBluetoothUtil.BluetoothProfile;
import com.android.tradefed.util.proto.TfMetricProtoUtil;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
/**
* The collector will push a pre-defined statsd duration metric config to devices and collect
* Bluetooth connection duration for each profile.
*/
@OptionClass(alias = "bluetooth-connection-latency-collector")
public class BluetoothConnectionLatencyCollector extends HostStatsdMetricCollector {
private static final String BLUETOOTH_CONNECTION_LATENCY_METRIC_KEY =
"bluetooth_connection_latency";
/** A map associates Bluetooth profile number to the descriptive name used for metric key. */
protected static final ImmutableMap<Integer, String> BLUETOOTH_PROFILES_MAP =
ImmutableMap.<Integer, String>builder()
.put(BluetoothProfile.HEADSET.getProfile(), "headset")
.put(BluetoothProfile.A2DP.getProfile(), "a2dp")
.put(BluetoothProfile.PAN.getProfile(), "pan")
.put(BluetoothProfile.MAP.getProfile(), "map")
.put(BluetoothProfile.A2DP_SINK.getProfile(), "a2dp_sink")
.put(BluetoothProfile.AVRCP_CONTROLLER.getProfile(), "avrcp_controller")
.put(BluetoothProfile.HEADSET_CLIENT.getProfile(), "headset_client")
.put(BluetoothProfile.PBAP_CLIENT.getProfile(), "pbap_client")
.put(BluetoothProfile.MAP_CLIENT.getProfile(), "map_client")
.build();
@Override
protected void processStatsReport(
ITestDevice device, InputStreamSource dataStream, DeviceMetricData runData) {
try {
ConfigMetricsReportList reports =
ConfigMetricsReportList.parseFrom(dataStream.createInputStream());
if (reports.getReportsCount() == 0) {
CLog.w("No stats report is collected");
return;
}
ConfigMetricsReport report = reports.getReports(0);
if (report.getMetricsCount() == 0) {
CLog.w("No metrics collected in stats report");
return;
}
DurationMetricDataWrapper durationData = report.getMetrics(0).getDurationMetrics();
if (durationData.getDataCount() == 0) {
CLog.w("No duration data collected");
return;
}
processBluetoothConnectionLatencyData(device, durationData, runData);
} catch (IOException e) {
CLog.e(
"Failed to process statsd metric report on device %s, error: %s",
device.getSerialNumber(), e);
}
}
/**
* Process bluetooth connection duration for each bluetooth profile.
*
* <p>It is assuming the metric report dumps a lis of duration values for each BT profile. And
* each duration value is corresponding to a time bucket during the test. It is also assuming
* the raw data is coming from a single BT connection.
*
* @param device to collect and process metrics
* @param durationData raw duration metrics pulled from device
* @param runData target object to store processed metrics
*/
private void processBluetoothConnectionLatencyData(
ITestDevice device, DurationMetricDataWrapper durationData, DeviceMetricData runData) {
for (DurationMetricData metric : durationData.getDataList()) {
long totalDurationNanos = 0;
for (DurationBucketInfo bucket : metric.getBucketInfoList()) {
totalDurationNanos += bucket.getDurationNanos();
}
int bluetoothProfile = metric.getDimensionLeafValuesInWhat(0).getValueInt();
String metricKey;
if (BLUETOOTH_PROFILES_MAP.containsKey(bluetoothProfile)) {
metricKey =
String.join(
"_",
BLUETOOTH_CONNECTION_LATENCY_METRIC_KEY,
BLUETOOTH_PROFILES_MAP.get(bluetoothProfile),
"ms");
} else {
metricKey =
String.join(
"_",
BLUETOOTH_CONNECTION_LATENCY_METRIC_KEY,
"profile",
String.valueOf(bluetoothProfile),
"ms");
}
long durationMs = totalDurationNanos / 1000000;
CLog.d(
"Processed metric on device %s with key: %s, value %d",
device.getSerialNumber(), metricKey, durationMs);
runData.addMetricForDevice(
device,
metricKey,
Metric.newBuilder(
TfMetricProtoUtil.stringToMetric(Double.toString(durationMs))));
}
}
}