Merge "Implement logging of upstream event type and duration."
This commit is contained in:
@@ -1861,6 +1861,7 @@ public class Tethering {
|
||||
mNotificationUpdater.onUpstreamCapabilitiesChanged(
|
||||
(ns != null) ? ns.networkCapabilities : null);
|
||||
}
|
||||
mTetheringMetrics.maybeUpdateUpstreamType(ns);
|
||||
}
|
||||
|
||||
protected void setUpstreamNetwork(UpstreamNetworkState ns) {
|
||||
@@ -2090,6 +2091,7 @@ public class Tethering {
|
||||
mNotificationUpdater.onUpstreamCapabilitiesChanged(null);
|
||||
}
|
||||
mBpfCoordinator.stopPolling();
|
||||
mTetheringMetrics.cleanup();
|
||||
}
|
||||
|
||||
private boolean updateUpstreamWanted() {
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
package com.android.networkstack.tethering.metrics;
|
||||
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
|
||||
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
||||
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
||||
import static android.net.TetheringManager.TETHERING_NCM;
|
||||
@@ -39,6 +46,8 @@ 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.annotation.Nullable;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.stats.connectivity.DownstreamType;
|
||||
import android.stats.connectivity.ErrorCode;
|
||||
import android.stats.connectivity.UpstreamType;
|
||||
@@ -49,6 +58,11 @@ import android.util.SparseArray;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.networkstack.tethering.UpstreamNetworkState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Collection of utilities for tethering metrics.
|
||||
*
|
||||
@@ -66,21 +80,58 @@ public class TetheringMetrics {
|
||||
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<>();
|
||||
private final SparseArray<Long> mDownstreamStartTime = new SparseArray<Long>();
|
||||
private final ArrayList<RecordUpstreamEvent> mUpstreamEventList = new ArrayList<>();
|
||||
private UpstreamType mCurrentUpstream = null;
|
||||
private Long mCurrentUpStreamStartTime = 0L;
|
||||
|
||||
/** Update Tethering stats about caller's package name and downstream type. */
|
||||
|
||||
/**
|
||||
* Return the current system time in milliseconds.
|
||||
* @return the current system time in milliseconds.
|
||||
*/
|
||||
public long timeNow() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static class RecordUpstreamEvent {
|
||||
public final long mStartTime;
|
||||
public final long mStopTime;
|
||||
public final UpstreamType mUpstreamType;
|
||||
|
||||
RecordUpstreamEvent(final long startTime, final long stopTime,
|
||||
final UpstreamType upstream) {
|
||||
mStartTime = startTime;
|
||||
mStopTime = stopTime;
|
||||
mUpstreamType = upstream;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a |NetworkTetheringReported.Builder| object to update the tethering stats for the
|
||||
* specified downstream type and caller's package name. Initializes the upstream events, error
|
||||
* code, and duration to default values. Sets the start time for the downstream type in the
|
||||
* |mDownstreamStartTime| map.
|
||||
* @param downstreamType The type of downstream connection (e.g. Wifi, USB, Bluetooth).
|
||||
* @param callerPkg The package name of the caller.
|
||||
*/
|
||||
public void createBuilder(final int downstreamType, final String callerPkg) {
|
||||
NetworkTetheringReported.Builder statsBuilder =
|
||||
NetworkTetheringReported.newBuilder();
|
||||
statsBuilder.setDownstreamType(downstreamTypeToEnum(downstreamType))
|
||||
NetworkTetheringReported.Builder statsBuilder = NetworkTetheringReported.newBuilder()
|
||||
.setDownstreamType(downstreamTypeToEnum(downstreamType))
|
||||
.setUserType(userTypeToEnum(callerPkg))
|
||||
.setUpstreamType(UpstreamType.UT_UNKNOWN)
|
||||
.setErrorCode(ErrorCode.EC_NO_ERROR)
|
||||
.setUpstreamEvents(UpstreamEvents.newBuilder())
|
||||
.setDurationMillis(0);
|
||||
mBuilderMap.put(downstreamType, statsBuilder);
|
||||
mDownstreamStartTime.put(downstreamType, timeNow());
|
||||
}
|
||||
|
||||
/** Update error code of given downstreamType. */
|
||||
/**
|
||||
* Update the error code of the given downstream type in the Tethering stats.
|
||||
* @param downstreamType The downstream type whose error code to update.
|
||||
* @param errCode The error code to set.
|
||||
*/
|
||||
public void updateErrorCode(final int downstreamType, final int errCode) {
|
||||
NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
|
||||
if (statsBuilder == null) {
|
||||
@@ -90,38 +141,155 @@ public class TetheringMetrics {
|
||||
statsBuilder.setErrorCode(errorCodeToEnum(errCode));
|
||||
}
|
||||
|
||||
/** Remove Tethering stats.
|
||||
* If Tethering stats is ready to write then write it before removing.
|
||||
/**
|
||||
* Update the list of upstream types and their duration whenever the current upstream type
|
||||
* changes.
|
||||
* @param ns The UpstreamNetworkState object representing the current upstream network state.
|
||||
*/
|
||||
public void maybeUpdateUpstreamType(@Nullable final UpstreamNetworkState ns) {
|
||||
UpstreamType upstream = transportTypeToUpstreamTypeEnum(ns);
|
||||
if (upstream.equals(mCurrentUpstream)) return;
|
||||
|
||||
final long newTime = timeNow();
|
||||
if (mCurrentUpstream != null) {
|
||||
mUpstreamEventList.add(new RecordUpstreamEvent(mCurrentUpStreamStartTime, newTime,
|
||||
mCurrentUpstream));
|
||||
}
|
||||
mCurrentUpstream = upstream;
|
||||
mCurrentUpStreamStartTime = newTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the greater of two start times.
|
||||
* @param first the first start time
|
||||
* @param second the second start time
|
||||
* @return the greater start time
|
||||
*/
|
||||
private long getGreaterStartTime(long first, long second) {
|
||||
return first > second ? first : second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the upstream events builder with a new upstream event.
|
||||
* @param upstreamEventsBuilder the builder for the upstream events list
|
||||
* @param start the start time of the upstream event
|
||||
* @param stop the stop time of the upstream event
|
||||
* @param upstream the type of upstream type (e.g. Wifi, Cellular, Bluetooth, ...)
|
||||
*/
|
||||
private void updateUpstreamEvents(final UpstreamEvents.Builder upstreamEventsBuilder,
|
||||
final long start, final long stop, @Nullable final UpstreamType upstream) {
|
||||
final UpstreamEvent.Builder upstreamEventBuilder = UpstreamEvent.newBuilder()
|
||||
.setUpstreamType(upstream == null ? UpstreamType.UT_NO_NETWORK : upstream)
|
||||
.setDurationMillis(stop - start);
|
||||
upstreamEventsBuilder.addUpstreamEvent(upstreamEventBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the |NetworkTetheringReported.Builder| with relevant upstream events associated with
|
||||
* the downstream event identified by the given downstream start time.
|
||||
*
|
||||
* This method iterates through the list of upstream events and adds any relevant events to a
|
||||
* |UpstreamEvents.Builder|. Upstream events are considered relevant if their stop time is
|
||||
* greater than or equal to the given downstream start time. The method also adds the last
|
||||
* upstream event that occurred up until the current time.
|
||||
*
|
||||
* The resulting |UpstreamEvents.Builder| is then added to the
|
||||
* |NetworkTetheringReported.Builder|, along with the duration of the downstream event
|
||||
* (i.e., stop time minus downstream start time).
|
||||
*
|
||||
* @param statsBuilder the builder for the NetworkTetheringReported message
|
||||
* @param downstreamStartTime the start time of the downstream event to find relevant upstream
|
||||
* events for
|
||||
*/
|
||||
private void updateStatsBuilderToWrite(final NetworkTetheringReported.Builder statsBuilder,
|
||||
final long downstreamStartTime) {
|
||||
UpstreamEvents.Builder upstreamEventsBuilder = UpstreamEvents.newBuilder();
|
||||
for (RecordUpstreamEvent event : mUpstreamEventList) {
|
||||
if (downstreamStartTime > event.mStopTime) continue;
|
||||
|
||||
final long startTime = getGreaterStartTime(downstreamStartTime, event.mStartTime);
|
||||
// Handle completed upstream events.
|
||||
updateUpstreamEvents(upstreamEventsBuilder, startTime, event.mStopTime,
|
||||
event.mUpstreamType);
|
||||
}
|
||||
final long startTime = getGreaterStartTime(downstreamStartTime, mCurrentUpStreamStartTime);
|
||||
final long stopTime = timeNow();
|
||||
// Handle the last upstream event.
|
||||
updateUpstreamEvents(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream);
|
||||
statsBuilder.setUpstreamEvents(upstreamEventsBuilder);
|
||||
statsBuilder.setDurationMillis(stopTime - downstreamStartTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes tethering statistics for the given downstream type. If there are any stats to write
|
||||
* for the downstream event associated with the type, they are written before removing the
|
||||
* statistics.
|
||||
*
|
||||
* If the given downstream type does not exist in the map, an error message is logged and the
|
||||
* method returns without doing anything.
|
||||
*
|
||||
* @param downstreamType the type of downstream event to remove statistics for
|
||||
*/
|
||||
public void sendReport(final int downstreamType) {
|
||||
final NetworkTetheringReported.Builder statsBuilder =
|
||||
mBuilderMap.get(downstreamType);
|
||||
final NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
|
||||
if (statsBuilder == null) {
|
||||
Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatsBuilderToWrite(statsBuilder, mDownstreamStartTime.get(downstreamType));
|
||||
write(statsBuilder.build());
|
||||
|
||||
mBuilderMap.remove(downstreamType);
|
||||
mDownstreamStartTime.remove(downstreamType);
|
||||
}
|
||||
|
||||
/** Collect Tethering stats and write metrics data to statsd pipeline. */
|
||||
/**
|
||||
* Collects tethering statistics and writes them to the statsd pipeline. This method takes in a
|
||||
* NetworkTetheringReported object, extracts its fields and uses them to write statistics data
|
||||
* to the statsd pipeline.
|
||||
*
|
||||
* @param reported a NetworkTetheringReported object containing statistics to write
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void write(@NonNull final NetworkTetheringReported reported) {
|
||||
TetheringStatsLog.write(TetheringStatsLog.NETWORK_TETHERING_REPORTED,
|
||||
final byte[] upstreamEvents = reported.getUpstreamEvents().toByteArray();
|
||||
|
||||
TetheringStatsLog.write(
|
||||
TetheringStatsLog.NETWORK_TETHERING_REPORTED,
|
||||
reported.getErrorCode().getNumber(),
|
||||
reported.getDownstreamType().getNumber(),
|
||||
reported.getUpstreamType().getNumber(),
|
||||
reported.getUserType().getNumber(),
|
||||
null, 0);
|
||||
upstreamEvents,
|
||||
reported.getDurationMillis());
|
||||
if (DBG) {
|
||||
Log.d(TAG, "Write errorCode: " + reported.getErrorCode().getNumber()
|
||||
+ ", downstreamType: " + reported.getDownstreamType().getNumber()
|
||||
+ ", upstreamType: " + reported.getUpstreamType().getNumber()
|
||||
+ ", userType: " + reported.getUserType().getNumber());
|
||||
Log.d(
|
||||
TAG,
|
||||
"Write errorCode: "
|
||||
+ reported.getErrorCode().getNumber()
|
||||
+ ", downstreamType: "
|
||||
+ reported.getDownstreamType().getNumber()
|
||||
+ ", upstreamType: "
|
||||
+ reported.getUpstreamType().getNumber()
|
||||
+ ", userType: "
|
||||
+ reported.getUserType().getNumber()
|
||||
+ ", upstreamTypes: "
|
||||
+ Arrays.toString(upstreamEvents)
|
||||
+ ", durationMillis: "
|
||||
+ reported.getDurationMillis());
|
||||
}
|
||||
}
|
||||
|
||||
/** Map {@link TetheringType} to {@link DownstreamType} */
|
||||
/**
|
||||
* Cleans up the variables related to upstream events when tethering is turned off.
|
||||
*/
|
||||
public void cleanup() {
|
||||
mUpstreamEventList.clear();
|
||||
mCurrentUpstream = null;
|
||||
mCurrentUpStreamStartTime = 0L;
|
||||
}
|
||||
|
||||
private DownstreamType downstreamTypeToEnum(final int ifaceType) {
|
||||
switch(ifaceType) {
|
||||
case TETHERING_WIFI:
|
||||
@@ -141,7 +309,6 @@ public class TetheringMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
/** Map {@link StartTetheringError} to {@link ErrorCode} */
|
||||
private ErrorCode errorCodeToEnum(final int lastError) {
|
||||
switch(lastError) {
|
||||
case TETHER_ERROR_NO_ERROR:
|
||||
@@ -181,7 +348,6 @@ public class TetheringMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
/** Map callerPkg to {@link UserType} */
|
||||
private UserType userTypeToEnum(final String callerPkg) {
|
||||
if (callerPkg.equals(SETTINGS_PKG_NAME)) {
|
||||
return UserType.USER_SETTINGS;
|
||||
@@ -193,4 +359,40 @@ public class TetheringMetrics {
|
||||
return UserType.USER_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
private UpstreamType transportTypeToUpstreamTypeEnum(final UpstreamNetworkState ns) {
|
||||
final NetworkCapabilities nc = (ns != null) ? ns.networkCapabilities : null;
|
||||
if (nc == null) return UpstreamType.UT_NO_NETWORK;
|
||||
|
||||
final int typeCount = nc.getTransportTypes().length;
|
||||
|
||||
boolean hasCellular = nc.hasTransport(TRANSPORT_CELLULAR);
|
||||
boolean hasWifi = nc.hasTransport(TRANSPORT_WIFI);
|
||||
boolean hasBT = nc.hasTransport(TRANSPORT_BLUETOOTH);
|
||||
boolean hasEthernet = nc.hasTransport(TRANSPORT_ETHERNET);
|
||||
boolean hasVpn = nc.hasTransport(TRANSPORT_VPN);
|
||||
boolean hasWifiAware = nc.hasTransport(TRANSPORT_WIFI_AWARE);
|
||||
boolean hasLopan = nc.hasTransport(TRANSPORT_LOWPAN);
|
||||
|
||||
if (typeCount == 3 && hasCellular && hasWifi && hasVpn) {
|
||||
return UpstreamType.UT_WIFI_CELLULAR_VPN;
|
||||
}
|
||||
|
||||
if (typeCount == 2 && hasVpn) {
|
||||
if (hasCellular) return UpstreamType.UT_CELLULAR_VPN;
|
||||
if (hasWifi) return UpstreamType.UT_WIFI_VPN;
|
||||
if (hasBT) return UpstreamType.UT_BLUETOOTH_VPN;
|
||||
if (hasEthernet) return UpstreamType.UT_ETHERNET_VPN;
|
||||
}
|
||||
|
||||
if (typeCount == 1) {
|
||||
if (hasCellular) return UpstreamType.UT_CELLULAR;
|
||||
if (hasWifi) return UpstreamType.UT_WIFI;
|
||||
if (hasBT) return UpstreamType.UT_BLUETOOTH;
|
||||
if (hasEthernet) return UpstreamType.UT_ETHERNET;
|
||||
if (hasWifiAware) return UpstreamType.UT_WIFI_AWARE;
|
||||
if (hasLopan) return UpstreamType.UT_LOWPAN;
|
||||
}
|
||||
return UpstreamType.UT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2000,6 +2000,7 @@ public class TetheringTest {
|
||||
verify(mWifiManager).updateInterfaceIpState(
|
||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
|
||||
|
||||
verify(mTetheringMetrics, times(0)).maybeUpdateUpstreamType(any());
|
||||
verify(mTetheringMetrics, times(2)).updateErrorCode(eq(TETHERING_WIFI),
|
||||
eq(TETHER_ERROR_INTERNAL_ERROR));
|
||||
verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_WIFI));
|
||||
@@ -3365,6 +3366,7 @@ public class TetheringTest {
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||
any(), any());
|
||||
verify(mTetheringMetrics).createBuilder(eq(TETHERING_NCM), anyString());
|
||||
verify(mTetheringMetrics, times(1)).maybeUpdateUpstreamType(any());
|
||||
|
||||
// Change the USB tethering function to NCM. Because the USB tethering function was set to
|
||||
// RNDIS (the default), tethering is stopped.
|
||||
@@ -3381,6 +3383,7 @@ public class TetheringTest {
|
||||
mLooper.dispatchAll();
|
||||
ncmResult.assertHasResult();
|
||||
verify(mTetheringMetrics, times(2)).createBuilder(eq(TETHERING_NCM), anyString());
|
||||
verify(mTetheringMetrics, times(1)).maybeUpdateUpstreamType(any());
|
||||
verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_NCM),
|
||||
eq(TETHER_ERROR_SERVICE_UNAVAIL));
|
||||
verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_NCM));
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
package com.android.networkstack.tethering.metrics;
|
||||
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
|
||||
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
||||
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
||||
import static android.net.TetheringManager.TETHERING_NCM;
|
||||
@@ -44,6 +51,7 @@ import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.stats.connectivity.DownstreamType;
|
||||
import android.stats.connectivity.ErrorCode;
|
||||
import android.stats.connectivity.UpstreamType;
|
||||
@@ -52,6 +60,8 @@ import android.stats.connectivity.UserType;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.networkstack.tethering.UpstreamNetworkState;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -64,46 +74,105 @@ public final class TetheringMetricsTest {
|
||||
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 static final long TEST_START_TIME = 1670395936033L;
|
||||
private static final long SECOND_IN_MILLIS = 1_000L;
|
||||
|
||||
private TetheringMetrics mTetheringMetrics;
|
||||
private final NetworkTetheringReported.Builder mStatsBuilder =
|
||||
NetworkTetheringReported.newBuilder();
|
||||
|
||||
private long mElapsedRealtime;
|
||||
|
||||
private class MockTetheringMetrics extends TetheringMetrics {
|
||||
@Override
|
||||
public void write(final NetworkTetheringReported reported) { }
|
||||
public void write(final NetworkTetheringReported reported) {}
|
||||
@Override
|
||||
public long timeNow() {
|
||||
return currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
private long currentTimeMillis() {
|
||||
return TEST_START_TIME + mElapsedRealtime;
|
||||
}
|
||||
|
||||
private void incrementCurrentTime(final long duration) {
|
||||
mElapsedRealtime += duration;
|
||||
mTetheringMetrics.timeNow();
|
||||
}
|
||||
|
||||
private long getElapsedRealtime() {
|
||||
return mElapsedRealtime;
|
||||
}
|
||||
|
||||
private void clearElapsedRealtime() {
|
||||
mElapsedRealtime = 0;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mTetheringMetrics = spy(new MockTetheringMetrics());
|
||||
mElapsedRealtime = 0L;
|
||||
}
|
||||
|
||||
private void verifyReport(DownstreamType downstream, ErrorCode error, UserType user)
|
||||
private void verifyReport(final DownstreamType downstream, final ErrorCode error,
|
||||
final UserType user, final UpstreamEvents.Builder upstreamEvents, final long duration)
|
||||
throws Exception {
|
||||
final NetworkTetheringReported expectedReport =
|
||||
mStatsBuilder.setDownstreamType(downstream)
|
||||
.setUserType(user)
|
||||
.setUpstreamType(UpstreamType.UT_UNKNOWN)
|
||||
.setErrorCode(error)
|
||||
.setUpstreamEvents(UpstreamEvents.newBuilder())
|
||||
.setDurationMillis(0)
|
||||
.setUpstreamEvents(upstreamEvents)
|
||||
.setDurationMillis(duration)
|
||||
.build();
|
||||
verify(mTetheringMetrics).write(expectedReport);
|
||||
}
|
||||
|
||||
private void updateErrorAndSendReport(int downstream, int error) {
|
||||
private void updateErrorAndSendReport(final int downstream, final int error) {
|
||||
mTetheringMetrics.updateErrorCode(downstream, error);
|
||||
mTetheringMetrics.sendReport(downstream);
|
||||
}
|
||||
|
||||
private static NetworkCapabilities buildUpstreamCapabilities(final int[] transports) {
|
||||
final NetworkCapabilities nc = new NetworkCapabilities();
|
||||
for (int type: transports) {
|
||||
nc.addTransportType(type);
|
||||
}
|
||||
return nc;
|
||||
}
|
||||
|
||||
private static UpstreamNetworkState buildUpstreamState(final int... transports) {
|
||||
return new UpstreamNetworkState(
|
||||
null,
|
||||
buildUpstreamCapabilities(transports),
|
||||
null);
|
||||
}
|
||||
|
||||
private void addUpstreamEvent(UpstreamEvents.Builder upstreamEvents,
|
||||
final UpstreamType expectedResult, final long duration) {
|
||||
UpstreamEvent.Builder upstreamEvent = UpstreamEvent.newBuilder()
|
||||
.setUpstreamType(expectedResult)
|
||||
.setDurationMillis(duration);
|
||||
upstreamEvents.addUpstreamEvent(upstreamEvent);
|
||||
}
|
||||
|
||||
private void runDownstreamTypesTest(final int type, final DownstreamType expectedResult)
|
||||
throws Exception {
|
||||
mTetheringMetrics.createBuilder(type, TEST_CALLER_PKG);
|
||||
final long duration = 2 * SECOND_IN_MILLIS;
|
||||
incrementCurrentTime(duration);
|
||||
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
|
||||
// Set UpstreamType as NO_NETWORK because the upstream type has not been changed.
|
||||
addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration);
|
||||
updateErrorAndSendReport(type, TETHER_ERROR_NO_ERROR);
|
||||
verifyReport(expectedResult, ErrorCode.EC_NO_ERROR, UserType.USER_UNKNOWN);
|
||||
|
||||
verifyReport(expectedResult, ErrorCode.EC_NO_ERROR, UserType.USER_UNKNOWN,
|
||||
upstreamEvents, getElapsedRealtime());
|
||||
reset(mTetheringMetrics);
|
||||
clearElapsedRealtime();
|
||||
mTetheringMetrics.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,8 +188,18 @@ public final class TetheringMetricsTest {
|
||||
private void runErrorCodesTest(final int errorCode, final ErrorCode expectedResult)
|
||||
throws Exception {
|
||||
mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
|
||||
mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
|
||||
final long duration = 2 * SECOND_IN_MILLIS;
|
||||
incrementCurrentTime(duration);
|
||||
updateErrorAndSendReport(TETHERING_WIFI, errorCode);
|
||||
verifyReport(DownstreamType.DS_TETHERING_WIFI, expectedResult, UserType.USER_UNKNOWN);
|
||||
|
||||
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
|
||||
addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, duration);
|
||||
verifyReport(DownstreamType.DS_TETHERING_WIFI, expectedResult, UserType.USER_UNKNOWN,
|
||||
upstreamEvents, getElapsedRealtime());
|
||||
reset(mTetheringMetrics);
|
||||
clearElapsedRealtime();
|
||||
mTetheringMetrics.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -151,9 +230,18 @@ public final class TetheringMetricsTest {
|
||||
private void runUserTypesTest(final String callerPkg, final UserType expectedResult)
|
||||
throws Exception {
|
||||
mTetheringMetrics.createBuilder(TETHERING_WIFI, callerPkg);
|
||||
final long duration = 1 * SECOND_IN_MILLIS;
|
||||
incrementCurrentTime(duration);
|
||||
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
|
||||
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR, expectedResult);
|
||||
|
||||
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
|
||||
// Set UpstreamType as NO_NETWORK because the upstream type has not been changed.
|
||||
addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration);
|
||||
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR, expectedResult,
|
||||
upstreamEvents, getElapsedRealtime());
|
||||
reset(mTetheringMetrics);
|
||||
clearElapsedRealtime();
|
||||
mTetheringMetrics.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -164,22 +252,113 @@ public final class TetheringMetricsTest {
|
||||
runUserTypesTest(GMS_PKG, UserType.USER_GMS);
|
||||
}
|
||||
|
||||
private void runUpstreamTypesTest(final UpstreamNetworkState ns,
|
||||
final UpstreamType expectedResult) throws Exception {
|
||||
mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
|
||||
mTetheringMetrics.maybeUpdateUpstreamType(ns);
|
||||
final long duration = 2 * SECOND_IN_MILLIS;
|
||||
incrementCurrentTime(duration);
|
||||
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
|
||||
|
||||
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
|
||||
addUpstreamEvent(upstreamEvents, expectedResult, duration);
|
||||
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
|
||||
UserType.USER_UNKNOWN, upstreamEvents, getElapsedRealtime());
|
||||
reset(mTetheringMetrics);
|
||||
clearElapsedRealtime();
|
||||
mTetheringMetrics.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpstreamTypes() throws Exception {
|
||||
runUpstreamTypesTest(null , UpstreamType.UT_NO_NETWORK);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR), UpstreamType.UT_CELLULAR);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI), UpstreamType.UT_WIFI);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_BLUETOOTH), UpstreamType.UT_BLUETOOTH);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET), UpstreamType.UT_ETHERNET);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI_AWARE), UpstreamType.UT_WIFI_AWARE);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_LOWPAN), UpstreamType.UT_LOWPAN);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_VPN),
|
||||
UpstreamType.UT_CELLULAR_VPN);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI, TRANSPORT_VPN),
|
||||
UpstreamType.UT_WIFI_VPN);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_BLUETOOTH, TRANSPORT_VPN),
|
||||
UpstreamType.UT_BLUETOOTH_VPN);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET, TRANSPORT_VPN),
|
||||
UpstreamType.UT_ETHERNET_VPN);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI, TRANSPORT_VPN),
|
||||
UpstreamType.UT_WIFI_CELLULAR_VPN);
|
||||
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI, TRANSPORT_VPN,
|
||||
TRANSPORT_BLUETOOTH), UpstreamType.UT_UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiBuildersCreatedBeforeSendReport() throws Exception {
|
||||
mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
|
||||
final long wifiTetheringStartTime = currentTimeMillis();
|
||||
incrementCurrentTime(1 * SECOND_IN_MILLIS);
|
||||
mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG);
|
||||
final long usbTetheringStartTime = currentTimeMillis();
|
||||
incrementCurrentTime(2 * SECOND_IN_MILLIS);
|
||||
mTetheringMetrics.createBuilder(TETHERING_BLUETOOTH, GMS_PKG);
|
||||
|
||||
final long bluetoothTetheringStartTime = currentTimeMillis();
|
||||
incrementCurrentTime(3 * SECOND_IN_MILLIS);
|
||||
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_DHCPSERVER_ERROR);
|
||||
|
||||
UpstreamEvents.Builder wifiTetheringUpstreamEvents = UpstreamEvents.newBuilder();
|
||||
addUpstreamEvent(wifiTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
|
||||
currentTimeMillis() - wifiTetheringStartTime);
|
||||
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_DHCPSERVER_ERROR,
|
||||
UserType.USER_SETTINGS);
|
||||
|
||||
UserType.USER_SETTINGS, wifiTetheringUpstreamEvents,
|
||||
currentTimeMillis() - wifiTetheringStartTime);
|
||||
incrementCurrentTime(1 * SECOND_IN_MILLIS);
|
||||
updateErrorAndSendReport(TETHERING_USB, TETHER_ERROR_ENABLE_FORWARDING_ERROR);
|
||||
verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_ENABLE_FORWARDING_ERROR,
|
||||
UserType.USER_SYSTEMUI);
|
||||
|
||||
UpstreamEvents.Builder usbTetheringUpstreamEvents = UpstreamEvents.newBuilder();
|
||||
addUpstreamEvent(usbTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
|
||||
currentTimeMillis() - usbTetheringStartTime);
|
||||
|
||||
verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_ENABLE_FORWARDING_ERROR,
|
||||
UserType.USER_SYSTEMUI, usbTetheringUpstreamEvents,
|
||||
currentTimeMillis() - usbTetheringStartTime);
|
||||
incrementCurrentTime(1 * SECOND_IN_MILLIS);
|
||||
updateErrorAndSendReport(TETHERING_BLUETOOTH, TETHER_ERROR_TETHER_IFACE_ERROR);
|
||||
|
||||
UpstreamEvents.Builder bluetoothTetheringUpstreamEvents = UpstreamEvents.newBuilder();
|
||||
addUpstreamEvent(bluetoothTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
|
||||
currentTimeMillis() - bluetoothTetheringStartTime);
|
||||
verifyReport(DownstreamType.DS_TETHERING_BLUETOOTH, ErrorCode.EC_TETHER_IFACE_ERROR,
|
||||
UserType.USER_GMS);
|
||||
UserType.USER_GMS, bluetoothTetheringUpstreamEvents,
|
||||
currentTimeMillis() - bluetoothTetheringStartTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpstreamsWithMultipleDownstreams() throws Exception {
|
||||
mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
|
||||
final long wifiTetheringStartTime = currentTimeMillis();
|
||||
incrementCurrentTime(1 * SECOND_IN_MILLIS);
|
||||
mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
|
||||
final long wifiUpstreamStartTime = currentTimeMillis();
|
||||
incrementCurrentTime(5 * SECOND_IN_MILLIS);
|
||||
mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG);
|
||||
final long usbTetheringStartTime = currentTimeMillis();
|
||||
incrementCurrentTime(5 * SECOND_IN_MILLIS);
|
||||
updateErrorAndSendReport(TETHERING_USB, TETHER_ERROR_NO_ERROR);
|
||||
|
||||
UpstreamEvents.Builder usbTetheringUpstreamEvents = UpstreamEvents.newBuilder();
|
||||
addUpstreamEvent(usbTetheringUpstreamEvents, UpstreamType.UT_WIFI,
|
||||
currentTimeMillis() - usbTetheringStartTime);
|
||||
verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_NO_ERROR,
|
||||
UserType.USER_SYSTEMUI, usbTetheringUpstreamEvents,
|
||||
currentTimeMillis() - usbTetheringStartTime);
|
||||
incrementCurrentTime(7 * SECOND_IN_MILLIS);
|
||||
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
|
||||
|
||||
UpstreamEvents.Builder wifiTetheringUpstreamEvents = UpstreamEvents.newBuilder();
|
||||
addUpstreamEvent(wifiTetheringUpstreamEvents, UpstreamType.UT_WIFI,
|
||||
currentTimeMillis() - wifiUpstreamStartTime);
|
||||
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
|
||||
UserType.USER_SETTINGS, wifiTetheringUpstreamEvents,
|
||||
currentTimeMillis() - wifiTetheringStartTime);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user