Injecting tethering stats into statsd

Fill in downstream type, upstream type, error code and user type to NetworkTetheringReported.

Bug: 153942334
Test: m, flash and boot
Test: atest TetheringMetricsTest
Change-Id: I6ba7d9e512b1ada519f44f9f3a95667e4b0f03c0
This commit is contained in:
Wayne Ma 2022-03-10 16:19:24 +08:00
commit 7ecded8248
3 changed files with 390 additions and 0 deletions

View file

@ -33,6 +33,7 @@ java_defaults {
":framework-connectivity-shared-srcs",
":tethering-module-utils-srcs",
":services-tethering-shared-srcs",
":statslog-tethering-java-gen",
],
static_libs: [
"androidx.annotation_annotation",
@ -223,3 +224,11 @@ java_library_static {
apex_available: ["com.android.tethering"],
min_sdk_version: "30",
}
genrule {
name: "statslog-tethering-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module network_tethering" +
" --javaPackage com.android.networkstack.tethering.metrics --javaClass TetheringStatsLog",
out: ["com/android/networkstack/tethering/metrics/TetheringStatsLog.java"],
}

View file

@ -0,0 +1,195 @@
/*
* Copyright (C) 2022 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.networkstack.tethering.metrics;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import android.stats.connectivity.DownstreamType;
import android.stats.connectivity.ErrorCode;
import android.stats.connectivity.UpstreamType;
import android.stats.connectivity.UserType;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
/**
* Collection of utilities for tethering metrics.
*
* To see if the logs are properly sent to statsd, execute following commands
*
* $ adb shell cmd stats print-logs
* $ adb logcat | grep statsd OR $ adb logcat -b stats
*
* @hide
*/
public class TetheringMetrics {
private static final String TAG = TetheringMetrics.class.getSimpleName();
private static final boolean DBG = false;
private static final String SETTINGS_PKG_NAME = "com.android.settings";
private static final String SYSTEMUI_PKG_NAME = "com.android.systemui";
private static final String GMS_PKG_NAME = "com.google.android.gms";
private final SparseArray<NetworkTetheringReported.Builder> mBuilderMap = new SparseArray<>();
/** Update Tethering stats about caller's package name and downstream type. */
public void createBuilder(final int downstreamType, final String callerPkg) {
mBuilderMap.clear();
NetworkTetheringReported.Builder statsBuilder =
NetworkTetheringReported.newBuilder();
statsBuilder.setDownstreamType(downstreamTypeToEnum(downstreamType))
.setUserType(userTypeToEnum(callerPkg))
.setUpstreamType(UpstreamType.UT_UNKNOWN)
.setErrorCode(ErrorCode.EC_NO_ERROR)
.build();
mBuilderMap.put(downstreamType, statsBuilder);
}
/** Update error code of given downstreamType. */
public void updateErrorCode(final int downstreamType, final int errCode) {
NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
if (statsBuilder == null) {
Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
return;
}
statsBuilder.setErrorCode(errorCodeToEnum(errCode));
}
/** Remove Tethering stats.
* If Tethering stats is ready to write then write it before removing.
*/
public void sendReport(final int downstreamType) {
final NetworkTetheringReported.Builder statsBuilder =
mBuilderMap.get(downstreamType);
if (statsBuilder == null) {
Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
return;
}
write(statsBuilder.build());
mBuilderMap.remove(downstreamType);
}
/** Collect Tethering stats and write metrics data to statsd pipeline. */
@VisibleForTesting
public void write(@NonNull final NetworkTetheringReported reported) {
TetheringStatsLog.write(TetheringStatsLog.NETWORK_TETHERING_REPORTED,
reported.getErrorCode().getNumber(),
reported.getDownstreamType().getNumber(),
reported.getUpstreamType().getNumber(),
reported.getUserType().getNumber());
if (DBG) {
Log.d(TAG, "Write errorCode: " + reported.getErrorCode().getNumber()
+ ", downstreamType: " + reported.getDownstreamType().getNumber()
+ ", upstreamType: " + reported.getUpstreamType().getNumber()
+ ", userType: " + reported.getUserType().getNumber());
}
}
/** Map {@link TetheringType} to {@link DownstreamType} */
private DownstreamType downstreamTypeToEnum(final int ifaceType) {
switch(ifaceType) {
case TETHERING_WIFI:
return DownstreamType.DS_TETHERING_WIFI;
case TETHERING_WIFI_P2P:
return DownstreamType.DS_TETHERING_WIFI_P2P;
case TETHERING_USB:
return DownstreamType.DS_TETHERING_USB;
case TETHERING_BLUETOOTH:
return DownstreamType.DS_TETHERING_BLUETOOTH;
case TETHERING_NCM:
return DownstreamType.DS_TETHERING_NCM;
case TETHERING_ETHERNET:
return DownstreamType.DS_TETHERING_ETHERNET;
default:
return DownstreamType.DS_UNSPECIFIED;
}
}
/** Map {@link StartTetheringError} to {@link ErrorCode} */
private ErrorCode errorCodeToEnum(final int lastError) {
switch(lastError) {
case TETHER_ERROR_NO_ERROR:
return ErrorCode.EC_NO_ERROR;
case TETHER_ERROR_UNKNOWN_IFACE:
return ErrorCode.EC_UNKNOWN_IFACE;
case TETHER_ERROR_SERVICE_UNAVAIL:
return ErrorCode.EC_SERVICE_UNAVAIL;
case TETHER_ERROR_UNSUPPORTED:
return ErrorCode.EC_UNSUPPORTED;
case TETHER_ERROR_UNAVAIL_IFACE:
return ErrorCode.EC_UNAVAIL_IFACE;
case TETHER_ERROR_INTERNAL_ERROR:
return ErrorCode.EC_INTERNAL_ERROR;
case TETHER_ERROR_TETHER_IFACE_ERROR:
return ErrorCode.EC_TETHER_IFACE_ERROR;
case TETHER_ERROR_UNTETHER_IFACE_ERROR:
return ErrorCode.EC_UNTETHER_IFACE_ERROR;
case TETHER_ERROR_ENABLE_FORWARDING_ERROR:
return ErrorCode.EC_ENABLE_FORWARDING_ERROR;
case TETHER_ERROR_DISABLE_FORWARDING_ERROR:
return ErrorCode.EC_DISABLE_FORWARDING_ERROR;
case TETHER_ERROR_IFACE_CFG_ERROR:
return ErrorCode.EC_IFACE_CFG_ERROR;
case TETHER_ERROR_PROVISIONING_FAILED:
return ErrorCode.EC_PROVISIONING_FAILED;
case TETHER_ERROR_DHCPSERVER_ERROR:
return ErrorCode.EC_DHCPSERVER_ERROR;
case TETHER_ERROR_ENTITLEMENT_UNKNOWN:
return ErrorCode.EC_ENTITLEMENT_UNKNOWN;
case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:
return ErrorCode.EC_NO_CHANGE_TETHERING_PERMISSION;
case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION:
return ErrorCode.EC_NO_ACCESS_TETHERING_PERMISSION;
default:
return ErrorCode.EC_UNKNOWN_TYPE;
}
}
/** Map callerPkg to {@link UserType} */
private UserType userTypeToEnum(final String callerPkg) {
if (callerPkg.equals(SETTINGS_PKG_NAME)) {
return UserType.USER_SETTINGS;
} else if (callerPkg.equals(SYSTEMUI_PKG_NAME)) {
return UserType.USER_SYSTEMUI;
} else if (callerPkg.equals(GMS_PKG_NAME)) {
return UserType.USER_GMS;
} else {
return UserType.USER_UNKNOWN;
}
}
}

View file

@ -0,0 +1,186 @@
/*
* Copyright (C) 2022 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.networkstack.tethering.metrics;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE;
import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.stats.connectivity.DownstreamType;
import android.stats.connectivity.ErrorCode;
import android.stats.connectivity.UpstreamType;
import android.stats.connectivity.UserType;
import android.util.Pair;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class TetheringMetricsTest {
private static final String TEST_CALLER_PKG = "com.test.caller.pkg";
private static final String SETTINGS_PKG = "com.android.settings";
private static final String SYSTEMUI_PKG = "com.android.systemui";
private static final String GMS_PKG = "com.google.android.gms";
private TetheringMetrics mTetheringMetrics;
private final NetworkTetheringReported.Builder mStatsBuilder =
NetworkTetheringReported.newBuilder();
private class MockTetheringMetrics extends TetheringMetrics {
@Override
public void write(final NetworkTetheringReported reported) { }
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mTetheringMetrics = spy(new MockTetheringMetrics());
}
private void runDownstreamTypesTest(final Pair<Integer, DownstreamType>... testPairs)
throws Exception {
for (Pair<Integer, DownstreamType> testPair : testPairs) {
final int type = testPair.first;
final DownstreamType expectedResult = testPair.second;
mTetheringMetrics.createBuilder(type, TEST_CALLER_PKG);
mTetheringMetrics.updateErrorCode(type, TETHER_ERROR_NO_ERROR);
mTetheringMetrics.sendReport(type);
NetworkTetheringReported expectedReport =
mStatsBuilder.setDownstreamType(expectedResult)
.setUserType(UserType.USER_UNKNOWN)
.setUpstreamType(UpstreamType.UT_UNKNOWN)
.setErrorCode(ErrorCode.EC_NO_ERROR)
.build();
verify(mTetheringMetrics).write(expectedReport);
reset(mTetheringMetrics);
}
}
@Test
public void testDownstreamTypes() throws Exception {
runDownstreamTypesTest(new Pair<>(TETHERING_WIFI, DownstreamType.DS_TETHERING_WIFI),
new Pair<>(TETHERING_WIFI_P2P, DownstreamType.DS_TETHERING_WIFI_P2P),
new Pair<>(TETHERING_BLUETOOTH, DownstreamType.DS_TETHERING_BLUETOOTH),
new Pair<>(TETHERING_USB, DownstreamType.DS_TETHERING_USB),
new Pair<>(TETHERING_NCM, DownstreamType.DS_TETHERING_NCM),
new Pair<>(TETHERING_ETHERNET, DownstreamType.DS_TETHERING_ETHERNET));
}
private void runErrorCodesTest(final Pair<Integer, ErrorCode>... testPairs)
throws Exception {
for (Pair<Integer, ErrorCode> testPair : testPairs) {
final int errorCode = testPair.first;
final ErrorCode expectedResult = testPair.second;
mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
mTetheringMetrics.updateErrorCode(TETHERING_WIFI, errorCode);
mTetheringMetrics.sendReport(TETHERING_WIFI);
NetworkTetheringReported expectedReport =
mStatsBuilder.setDownstreamType(DownstreamType.DS_TETHERING_WIFI)
.setUserType(UserType.USER_UNKNOWN)
.setUpstreamType(UpstreamType.UT_UNKNOWN)
.setErrorCode(expectedResult)
.build();
verify(mTetheringMetrics).write(expectedReport);
reset(mTetheringMetrics);
}
}
@Test
public void testErrorCodes() throws Exception {
runErrorCodesTest(new Pair<>(TETHER_ERROR_NO_ERROR, ErrorCode.EC_NO_ERROR),
new Pair<>(TETHER_ERROR_UNKNOWN_IFACE, ErrorCode.EC_UNKNOWN_IFACE),
new Pair<>(TETHER_ERROR_SERVICE_UNAVAIL, ErrorCode.EC_SERVICE_UNAVAIL),
new Pair<>(TETHER_ERROR_UNSUPPORTED, ErrorCode.EC_UNSUPPORTED),
new Pair<>(TETHER_ERROR_UNAVAIL_IFACE, ErrorCode.EC_UNAVAIL_IFACE),
new Pair<>(TETHER_ERROR_INTERNAL_ERROR, ErrorCode.EC_INTERNAL_ERROR),
new Pair<>(TETHER_ERROR_TETHER_IFACE_ERROR, ErrorCode.EC_TETHER_IFACE_ERROR),
new Pair<>(TETHER_ERROR_UNTETHER_IFACE_ERROR, ErrorCode.EC_UNTETHER_IFACE_ERROR),
new Pair<>(TETHER_ERROR_ENABLE_FORWARDING_ERROR,
ErrorCode.EC_ENABLE_FORWARDING_ERROR),
new Pair<>(TETHER_ERROR_DISABLE_FORWARDING_ERROR,
ErrorCode.EC_DISABLE_FORWARDING_ERROR),
new Pair<>(TETHER_ERROR_IFACE_CFG_ERROR, ErrorCode.EC_IFACE_CFG_ERROR),
new Pair<>(TETHER_ERROR_PROVISIONING_FAILED, ErrorCode.EC_PROVISIONING_FAILED),
new Pair<>(TETHER_ERROR_DHCPSERVER_ERROR, ErrorCode.EC_DHCPSERVER_ERROR),
new Pair<>(TETHER_ERROR_ENTITLEMENT_UNKNOWN, ErrorCode.EC_ENTITLEMENT_UNKNOWN),
new Pair<>(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION,
ErrorCode.EC_NO_CHANGE_TETHERING_PERMISSION),
new Pair<>(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION,
ErrorCode.EC_NO_ACCESS_TETHERING_PERMISSION),
new Pair<>(TETHER_ERROR_UNKNOWN_TYPE, ErrorCode.EC_UNKNOWN_TYPE));
}
private void runUserTypesTest(final Pair<String, UserType>... testPairs)
throws Exception {
for (Pair<String, UserType> testPair : testPairs) {
final String callerPkg = testPair.first;
final UserType expectedResult = testPair.second;
mTetheringMetrics.createBuilder(TETHERING_WIFI, callerPkg);
mTetheringMetrics.updateErrorCode(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
mTetheringMetrics.sendReport(TETHERING_WIFI);
NetworkTetheringReported expectedReport =
mStatsBuilder.setDownstreamType(DownstreamType.DS_TETHERING_WIFI)
.setUserType(expectedResult)
.setUpstreamType(UpstreamType.UT_UNKNOWN)
.setErrorCode(ErrorCode.EC_NO_ERROR)
.build();
verify(mTetheringMetrics).write(expectedReport);
reset(mTetheringMetrics);
}
}
@Test
public void testUserTypes() throws Exception {
runUserTypesTest(new Pair<>(TEST_CALLER_PKG, UserType.USER_UNKNOWN),
new Pair<>(SETTINGS_PKG, UserType.USER_SETTINGS),
new Pair<>(SYSTEMUI_PKG, UserType.USER_SYSTEMUI),
new Pair<>(GMS_PKG, UserType.USER_GMS));
}
}