Merge changes from topic "tethering_metrics"

* changes:
  Add tethering stats into statsd
  Injecting tethering stats into statsd
This commit is contained in:
Treehugger Robot
2022-05-16 05:06:01 +00:00
committed by Gerrit Code Review
10 changed files with 493 additions and 30 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

@@ -69,6 +69,7 @@ import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import com.android.networkstack.tethering.TetheringConfiguration;
import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
@@ -282,13 +283,15 @@ public class IpServer extends StateMachine {
private LinkAddress mIpv4Address;
private final TetheringMetrics mTetheringMetrics;
// TODO: Add a dependency object to pass the data members or variables from the tethering
// object. It helps to reduce the arguments of the constructor.
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator,
Dependencies deps) {
TetheringMetrics tetheringMetrics, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
@@ -303,6 +306,7 @@ public class IpServer extends StateMachine {
mP2pLeasesSubnetPrefixLength = config.getP2pLeasesSubnetPrefixLength();
mPrivateAddressCoordinator = addressCoordinator;
mDeps = deps;
mTetheringMetrics = tetheringMetrics;
resetLinkProperties();
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
mServingMode = STATE_AVAILABLE;
@@ -1201,6 +1205,9 @@ public class IpServer extends StateMachine {
stopConntrackMonitoring();
resetLinkProperties();
mTetheringMetrics.updateErrorCode(mInterfaceType, mLastError);
mTetheringMetrics.sendReport(mInterfaceType);
}
@Override

View File

@@ -140,6 +140,7 @@ import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
import com.android.networkstack.tethering.util.TetheringUtils;
@@ -254,6 +255,7 @@ public class Tethering {
private final UserManager mUserManager;
private final BpfCoordinator mBpfCoordinator;
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
private final TetheringMetrics mTetheringMetrics;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
private volatile TetheringConfiguration mConfig;
@@ -292,6 +294,7 @@ public class Tethering {
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
mTetheringMetrics = mDeps.getTetheringMetrics();
// This is intended to ensrure that if something calls startTethering(bluetooth) just after
// bluetooth is enabled. Before onServiceConnected is called, store the calls into this
@@ -616,7 +619,8 @@ public class Tethering {
processInterfaceStateChange(iface, false /* enabled */);
}
void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
void startTethering(final TetheringRequestParcel request, final String callerPkg,
final IIntResultListener listener) {
mHandler.post(() -> {
final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
request.tetheringType);
@@ -636,6 +640,7 @@ public class Tethering {
request.showProvisioningUi);
}
enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
mTetheringMetrics.createBuilder(request.tetheringType, callerPkg);
});
}
@@ -695,7 +700,11 @@ public class Tethering {
// If changing tethering fail, remove corresponding request
// no matter who trigger the start/stop.
if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type);
if (result != TETHER_ERROR_NO_ERROR) {
mActiveTetheringRequests.remove(type);
mTetheringMetrics.updateErrorCode(type, result);
mTetheringMetrics.sendReport(type);
}
}
private int setWifiTethering(final boolean enable) {
@@ -2749,7 +2758,7 @@ public class Tethering {
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig, mPrivateAddressCoordinator,
mDeps.getIpServerDependencies()), isNcm);
mTetheringMetrics, mDeps.getIpServerDependencies()), isNcm);
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}

View File

@@ -34,6 +34,7 @@ import androidx.annotation.NonNull;
import com.android.internal.util.StateMachine;
import com.android.networkstack.apishim.BluetoothPanShimImpl;
import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.tethering.metrics.TetheringMetrics;
import java.util.ArrayList;
@@ -163,4 +164,11 @@ public abstract class TetheringDependencies {
public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
return BluetoothPanShimImpl.newInstance(pan);
}
/**
* Get a reference to the TetheringMetrics to be used by tethering.
*/
public TetheringMetrics getTetheringMetrics() {
return new TetheringMetrics();
}
}

View File

@@ -137,7 +137,7 @@ public class TetheringService extends Service {
return;
}
mTethering.startTethering(request, listener);
mTethering.startTethering(request, callerPkg, listener);
}
@Override

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

@@ -116,6 +116,7 @@ import com.android.networkstack.tethering.TetherLimitKey;
import com.android.networkstack.tethering.TetherLimitValue;
import com.android.networkstack.tethering.TetherUpstream6Key;
import com.android.networkstack.tethering.TetheringConfiguration;
import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
import com.android.testutils.DevSdkIgnoreRule;
@@ -186,6 +187,7 @@ public class IpServerTest {
@Mock private NetworkStatsManager mStatsManager;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private ConntrackMonitor mConntrackMonitor;
@Mock private TetheringMetrics mTetheringMetrics;
@Mock private BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map;
@Mock private BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map;
@Mock private BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
@@ -235,7 +237,7 @@ public class IpServerTest {
when(mTetherConfig.getP2pLeasesSubnetPrefixLength()).thenReturn(P2P_SUBNET_PREFIX_LENGTH);
mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator,
mCallback, mTetherConfig, mAddressCoordinator, mDependencies);
mCallback, mTetherConfig, mAddressCoordinator, mTetheringMetrics, mDependencies);
mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue();
@@ -367,7 +369,7 @@ public class IpServerTest {
.thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
mNetd, mBpfCoordinator, mCallback, mTetherConfig, mAddressCoordinator,
mDependencies);
mTetheringMetrics, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(
@@ -451,6 +453,9 @@ public class IpServerTest {
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_BLUETOOTH),
eq(TETHER_ERROR_NO_ERROR));
verify(mTetheringMetrics).sendReport(eq(TETHERING_BLUETOOTH));
verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
}
@@ -658,6 +663,9 @@ public class IpServerTest {
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_USB),
eq(TETHER_ERROR_TETHER_IFACE_ERROR));
verify(mTetheringMetrics).sendReport(eq(TETHERING_USB));
}
@Test
@@ -676,6 +684,9 @@ public class IpServerTest {
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_USB),
eq(TETHER_ERROR_ENABLE_FORWARDING_ERROR));
verify(mTetheringMetrics).sendReport(eq(TETHERING_USB));
}
@Test

View File

@@ -275,7 +275,7 @@ public final class TetheringServiceTest {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
verify(mTethering).isTetheringSupported();
verify(mTethering).startTethering(eq(request), eq(result));
verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result));
}
@Test

View File

@@ -45,6 +45,7 @@ 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_IFACE_CFG_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
@@ -192,6 +193,7 @@ import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfac
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.testutils.MiscAsserts;
import org.junit.After;
@@ -239,6 +241,7 @@ public class TetheringTest {
private static final String TEST_WIFI_REGEX = "test_wlan\\d";
private static final String TEST_P2P_REGEX = "test_p2p-p2p\\d-.*";
private static final String TEST_BT_REGEX = "test_pan\\d";
private static final String TEST_CALLER_PKG = "com.test.tethering";
private static final int CELLULAR_NETID = 100;
private static final int WIFI_NETID = 101;
@@ -273,6 +276,7 @@ public class TetheringTest {
@Mock private BluetoothPan mBluetoothPan;
@Mock private BluetoothPanShim mBluetoothPanShim;
@Mock private TetheredInterfaceRequestShim mTetheredInterfaceRequestShim;
@Mock private TetheringMetrics mTetheringMetrics;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -496,6 +500,11 @@ public class TetheringTest {
return false;
}
@Override
public TetheringMetrics getTetheringMetrics() {
return mTetheringMetrics;
}
@Override
public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
TetheringConfiguration cfg) {
@@ -855,7 +864,8 @@ public class TetheringTest {
private void prepareNcmTethering() {
// Emulate startTethering(TETHERING_NCM) called
mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
}
@@ -863,7 +873,7 @@ public class TetheringTest {
private void prepareUsbTethering() {
// Emulate pressing the USB tethering button in Settings UI.
final TetheringRequestParcel request = createTetheringRequestParcel(TETHERING_USB);
mTethering.startTethering(request, null);
mTethering.startTethering(request, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
assertEquals(1, mTethering.getActiveTetheringRequests().size());
@@ -1433,7 +1443,8 @@ public class TetheringTest {
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -1460,7 +1471,8 @@ public class TetheringTest {
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -1536,11 +1548,13 @@ public class TetheringTest {
doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
// Emulate pressing the WiFi tethering button.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
verifyNoMoreInteractions(mNetd);
verify(mTetheringMetrics).createBuilder(eq(TETHERING_WIFI), anyString());
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
@@ -1579,6 +1593,10 @@ public class TetheringTest {
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
verify(mTetheringMetrics, times(2)).updateErrorCode(eq(TETHERING_WIFI),
eq(TETHER_ERROR_INTERNAL_ERROR));
verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_WIFI));
verifyNoMoreInteractions(mWifiManager);
verifyNoMoreInteractions(mNetd);
}
@@ -1881,7 +1899,8 @@ public class TetheringTest {
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
null);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface});
@@ -1978,10 +1997,12 @@ public class TetheringTest {
public void testNoDuplicatedEthernetRequest() throws Exception {
final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mEm, times(1)).requestTetheredInterface(any(), any());
mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verifyNoMoreInteractions(mEm);
mTethering.stopTethering(TETHERING_ETHERNET);
@@ -2185,14 +2206,16 @@ public class TetheringTest {
final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR);
// Enable USB tethering and check that Tethering starts USB.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), firstResult);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), TEST_CALLER_PKG,
firstResult);
mLooper.dispatchAll();
firstResult.assertHasResult();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
verifyNoMoreInteractions(mUsbManager);
// Enable USB tethering again with the same request and expect no change to USB.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), secondResult);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), TEST_CALLER_PKG,
secondResult);
mLooper.dispatchAll();
secondResult.assertHasResult();
verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE);
@@ -2201,7 +2224,8 @@ public class TetheringTest {
// Enable USB tethering with a different request and expect that USB is stopped and
// started.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), thirdResult);
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL),
TEST_CALLER_PKG, thirdResult);
mLooper.dispatchAll();
thirdResult.assertHasResult();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
@@ -2230,7 +2254,8 @@ public class TetheringTest {
final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor =
ArgumentCaptor.forClass(DhcpServingParamsParcel.class);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), null);
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL),
TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
@@ -2298,7 +2323,7 @@ public class TetheringTest {
final TetheringRequestParcel wifiNotExemptRequest =
createTetheringRequestParcel(TETHERING_WIFI, null, null, false,
CONNECTIVITY_SCOPE_GLOBAL);
mTethering.startTethering(wifiNotExemptRequest, null);
mTethering.startTethering(wifiNotExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
@@ -2312,7 +2337,7 @@ public class TetheringTest {
final TetheringRequestParcel wifiExemptRequest =
createTetheringRequestParcel(TETHERING_WIFI, null, null, true,
CONNECTIVITY_SCOPE_GLOBAL);
mTethering.startTethering(wifiExemptRequest, null);
mTethering.startTethering(wifiExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
@@ -2325,14 +2350,14 @@ public class TetheringTest {
// If one app enables tethering without provisioning check first, then another app enables
// tethering of the same type but does not disable the provisioning check.
setupForRequiredProvisioning();
mTethering.startTethering(wifiExemptRequest, null);
mTethering.startTethering(wifiExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
assertTrue(mEntitleMgr.isCellularUpstreamPermitted());
reset(mEntitleMgr);
setupForRequiredProvisioning();
mTethering.startTethering(wifiNotExemptRequest, null);
mTethering.startTethering(wifiNotExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
@@ -2422,7 +2447,8 @@ public class TetheringTest {
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor =
ArgumentCaptor.forClass(TetheredInterfaceCallback.class);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET),
TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
TetheredInterfaceCallback ethCallback = callbackCaptor.getValue();
@@ -2597,7 +2623,8 @@ public class TetheringTest {
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, result);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
result.assertHasResult();
@@ -2632,7 +2659,8 @@ public class TetheringTest {
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, result);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
result.assertHasResult();
@@ -2653,7 +2681,8 @@ public class TetheringTest {
// already bound.
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), secondResult);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, secondResult);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, false /* bindToPanService */);
secondResult.assertHasResult();
@@ -2674,7 +2703,8 @@ public class TetheringTest {
public void testBluetoothServiceDisconnects() throws Exception {
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, result);
mLooper.dispatchAll();
ServiceListener panListener = verifySetBluetoothTethering(true /* enable */,
true /* bindToPanService */);
@@ -2825,18 +2855,26 @@ public class TetheringTest {
runNcmTethering();
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
verify(mTetheringMetrics).createBuilder(eq(TETHERING_NCM), anyString());
// Change the USB tethering function to NCM. Because the USB tethering function was set to
// RNDIS (the default), tethering is stopped.
forceUsbTetheringUse(TETHER_USB_NCM_FUNCTION);
verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
verify(mTetheringMetrics).updateErrorCode(anyInt(), eq(TETHER_ERROR_NO_ERROR));
verify(mTetheringMetrics).sendReport(eq(TETHERING_NCM));
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
// available.
final ResultListener ncmResult = new ResultListener(TETHER_ERROR_SERVICE_UNAVAIL);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), ncmResult);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), TEST_CALLER_PKG,
ncmResult);
mLooper.dispatchAll();
ncmResult.assertHasResult();
verify(mTetheringMetrics, times(2)).createBuilder(eq(TETHERING_NCM), anyString());
verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_NCM),
eq(TETHER_ERROR_SERVICE_UNAVAIL));
verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_NCM));
// Run TETHERING_USB with ncm configuration.
runDualStackUsbTethering(TEST_NCM_IFNAME);

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));
}
}