Merge "Revert "Importing the following changes from gmscore""
diff --git a/framework/src/android/net/NattKeepalivePacketData.java b/framework/src/android/net/NattKeepalivePacketData.java
index a18e713..a524859 100644
--- a/framework/src/android/net/NattKeepalivePacketData.java
+++ b/framework/src/android/net/NattKeepalivePacketData.java
@@ -29,7 +29,9 @@
import com.android.net.module.util.IpUtils;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Objects;
@@ -38,6 +40,7 @@
@SystemApi
public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
private static final int IPV4_HEADER_LENGTH = 20;
+ private static final int IPV6_HEADER_LENGTH = 40;
private static final int UDP_HEADER_LENGTH = 8;
// This should only be constructed via static factory methods, such as
@@ -59,13 +62,25 @@
throw new InvalidPacketException(ERROR_INVALID_PORT);
}
- if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
+ // Convert IPv4 mapped v6 address to v4 if any.
+ final InetAddress srcAddr, dstAddr;
+ try {
+ srcAddr = InetAddress.getByAddress(srcAddress.getAddress());
+ dstAddr = InetAddress.getByAddress(dstAddress.getAddress());
+ } catch (UnknownHostException e) {
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
}
- return nattKeepalivePacketv4(
- (Inet4Address) srcAddress, srcPort,
- (Inet4Address) dstAddress, dstPort);
+ if (srcAddr instanceof Inet4Address && dstAddr instanceof Inet4Address) {
+ return nattKeepalivePacketv4(
+ (Inet4Address) srcAddr, srcPort, (Inet4Address) dstAddr, dstPort);
+ } else if (srcAddr instanceof Inet6Address && dstAddress instanceof Inet6Address) {
+ return nattKeepalivePacketv6(
+ (Inet6Address) srcAddr, srcPort, (Inet6Address) dstAddr, dstPort);
+ } else {
+ // Destination address and source address should be the same IP family.
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
}
private static NattKeepalivePacketData nattKeepalivePacketv4(
@@ -82,14 +97,14 @@
// /proc/sys/net/ipv4/ip_default_ttl. Use hard-coded 64 for simplicity.
buf.put((byte) 64); // TTL
buf.put((byte) OsConstants.IPPROTO_UDP);
- int ipChecksumOffset = buf.position();
+ final int ipChecksumOffset = buf.position();
buf.putShort((short) 0); // IP checksum
buf.put(srcAddress.getAddress());
buf.put(dstAddress.getAddress());
buf.putShort((short) srcPort);
buf.putShort((short) dstPort);
buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length
- int udpChecksumOffset = buf.position();
+ final int udpChecksumOffset = buf.position();
buf.putShort((short) 0); // UDP checksum
buf.put((byte) 0xff); // NAT-T keepalive
buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
@@ -98,6 +113,30 @@
return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
}
+ private static NattKeepalivePacketData nattKeepalivePacketv6(
+ Inet6Address srcAddress, int srcPort, Inet6Address dstAddress, int dstPort)
+ throws InvalidPacketException {
+ final ByteBuffer buf = ByteBuffer.allocate(IPV6_HEADER_LENGTH + UDP_HEADER_LENGTH + 1);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.putInt(0x60000000); // IP version, traffic class and flow label
+ buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // Payload length
+ buf.put((byte) OsConstants.IPPROTO_UDP); // Next header
+ // For native ipv6, this hop limit value should use the per interface v6 hoplimit sysctl.
+ // For 464xlat, this value should use the v4 ttl sysctl.
+ // Either way, for simplicity, just hard code 64.
+ buf.put((byte) 64); // Hop limit
+ buf.put(srcAddress.getAddress());
+ buf.put(dstAddress.getAddress());
+ // UDP
+ buf.putShort((short) srcPort);
+ buf.putShort((short) dstPort);
+ buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length = Payload length
+ final int udpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // UDP checksum
+ buf.put((byte) 0xff); // NAT-T keepalive. 1 byte of data
+ buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV6_HEADER_LENGTH));
+ return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
+ }
/** Parcelable Implementation */
public int describeContents() {
return 0;
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 2adc028..6ba2033 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -94,6 +94,7 @@
private static final int ADJUST_TCP_POLLING_DELAY_MS = 2000;
private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
"automatic_on_off_keepalive_version";
+ public static final long METRICS_COLLECTION_DURATION_MS = 24 * 60 * 60 * 1_000L;
// ConnectivityService parses message constants from itself and AutomaticOnOffKeepaliveTracker
// with MessageUtils for debugging purposes, and crashes if some messages have the same values.
@@ -180,6 +181,9 @@
private final LocalLog mEventLog = new LocalLog(MAX_EVENTS_LOGS);
private final KeepaliveStatsTracker mKeepaliveStatsTracker;
+
+ private final long mMetricsWriteTimeBase;
+
/**
* Information about a managed keepalive.
*
@@ -248,7 +252,7 @@
}
public Network getNetwork() {
- return mKi.getNai().network;
+ return mKi.getNai().network();
}
@Nullable
@@ -311,7 +315,26 @@
mContext, mConnectivityServiceHandler);
mAlarmManager = mDependencies.getAlarmManager(context);
- mKeepaliveStatsTracker = new KeepaliveStatsTracker(handler);
+ mKeepaliveStatsTracker =
+ mDependencies.newKeepaliveStatsTracker(context, handler);
+
+ final long time = mDependencies.getElapsedRealtime();
+ mMetricsWriteTimeBase = time % METRICS_COLLECTION_DURATION_MS;
+ final long triggerAtMillis = mMetricsWriteTimeBase + METRICS_COLLECTION_DURATION_MS;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, TAG,
+ this::writeMetricsAndRescheduleAlarm, handler);
+ }
+
+ private void writeMetricsAndRescheduleAlarm() {
+ mKeepaliveStatsTracker.writeAndResetMetrics();
+
+ final long time = mDependencies.getElapsedRealtime();
+ final long triggerAtMillis =
+ mMetricsWriteTimeBase
+ + (time - time % METRICS_COLLECTION_DURATION_MS)
+ + METRICS_COLLECTION_DURATION_MS;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, TAG,
+ this::writeMetricsAndRescheduleAlarm, mConnectivityServiceHandler);
}
private void startTcpPollingAlarm(@NonNull AutomaticOnOffKeepalive ki) {
@@ -455,7 +478,13 @@
return;
}
mEventLog.log("Start keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
- mKeepaliveStatsTracker.onStartKeepalive();
+ mKeepaliveStatsTracker.onStartKeepalive(
+ autoKi.getNetwork(),
+ autoKi.mKi.getSlot(),
+ autoKi.mKi.getNai().networkCapabilities,
+ autoKi.mKi.getKeepaliveIntervalSec(),
+ autoKi.mKi.getUid(),
+ STATE_ALWAYS_ON != autoKi.mAutomaticOnOffState);
// Add automatic on/off request into list to track its life cycle.
try {
@@ -483,7 +512,7 @@
+ " with error " + error);
return error;
}
- mKeepaliveStatsTracker.onResumeKeepalive();
+ mKeepaliveStatsTracker.onResumeKeepalive(ki.getNai().network(), ki.getSlot());
mEventLog.log("Resumed successfully keepalive " + ki.mCallback + " on " + ki.mNai);
return SUCCESS;
@@ -491,7 +520,7 @@
private void handlePauseKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
mEventLog.log("Suspend keepalive " + ki.mCallback + " on " + ki.mNai);
- mKeepaliveStatsTracker.onPauseKeepalive();
+ mKeepaliveStatsTracker.onPauseKeepalive(ki.getNai().network(), ki.getSlot());
// TODO : mKT.handleStopKeepalive should take a KeepaliveInfo instead
mKeepaliveTracker.handleStopKeepalive(ki.getNai(), ki.getSlot(), SUCCESS_PAUSED);
}
@@ -515,7 +544,7 @@
private void cleanupAutoOnOffKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi) {
ensureRunningOnHandlerThread();
- mKeepaliveStatsTracker.onStopKeepalive(autoKi.mAutomaticOnOffState != STATE_SUSPENDED);
+ mKeepaliveStatsTracker.onStopKeepalive(autoKi.getNetwork(), autoKi.mKi.getSlot());
autoKi.close();
if (null != autoKi.mAlarmListener) mAlarmManager.cancel(autoKi.mAlarmListener);
@@ -892,6 +921,14 @@
}
/**
+ * Construct a new KeepaliveStatsTracker.
+ */
+ public KeepaliveStatsTracker newKeepaliveStatsTracker(@NonNull Context context,
+ @NonNull Handler connectivityserviceHander) {
+ return new KeepaliveStatsTracker(context, connectivityserviceHander);
+ }
+
+ /**
* Find out if a feature is enabled from DeviceConfig.
*
* @param name The name of the property to look up.
diff --git a/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
index 07140c4..d59d526 100644
--- a/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
@@ -16,21 +16,46 @@
package com.android.server.connectivity;
+import static android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.TransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.SystemClock;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.metrics.DailykeepaliveInfoReported;
import com.android.metrics.DurationForNumOfKeepalive;
import com.android.metrics.DurationPerNumOfKeepalive;
+import com.android.metrics.KeepaliveLifetimeForCarrier;
+import com.android.metrics.KeepaliveLifetimePerCarrier;
+import com.android.modules.utils.BackgroundThread;
+import com.android.net.module.util.CollectionUtils;
+import com.android.server.ConnectivityStatsLog;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Set;
-// TODO(b/273451360): Also track KeepaliveLifetimeForCarrier and DailykeepaliveInfoReported
/**
* Tracks carrier and duration metrics of automatic on/off keepalives.
*
@@ -44,6 +69,111 @@
@NonNull private final Handler mConnectivityServiceHandler;
@NonNull private final Dependencies mDependencies;
+ // Mapping of subId to carrierId. Updates are received from OnSubscriptionsChangedListener
+ private final SparseIntArray mCachedCarrierIdPerSubId = new SparseIntArray();
+ // The default subscription id obtained from SubscriptionManager.getDefaultSubscriptionId.
+ // Updates are received from the ACTION_DEFAULT_SUBSCRIPTION_CHANGED broadcast.
+ private int mCachedDefaultSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ // Class to store network information, lifetime durations and active state of a keepalive.
+ private static final class KeepaliveStats {
+ // The carrier ID for a keepalive, or TelephonyManager.UNKNOWN_CARRIER_ID(-1) if not set.
+ public final int carrierId;
+ // The transport types of the underlying network for each keepalive. A network may include
+ // multiple transport types. Each transport type is represented by a different bit, defined
+ // in NetworkCapabilities
+ public final int transportTypes;
+ // The keepalive interval in millis.
+ public final int intervalMs;
+ // The uid of the app that requested the keepalive.
+ public final int appUid;
+ // Indicates if the keepalive is an automatic keepalive.
+ public final boolean isAutoKeepalive;
+
+ // Snapshot of the lifetime stats
+ public static class LifetimeStats {
+ public final int lifetimeMs;
+ public final int activeLifetimeMs;
+
+ LifetimeStats(int lifetimeMs, int activeLifetimeMs) {
+ this.lifetimeMs = lifetimeMs;
+ this.activeLifetimeMs = activeLifetimeMs;
+ }
+ }
+
+ // The total time since the keepalive is started until it is stopped.
+ private int mLifetimeMs = 0;
+ // The total time the keepalive is active (not suspended).
+ private int mActiveLifetimeMs = 0;
+
+ // A timestamp of the most recent time the lifetime metrics was updated.
+ private long mLastUpdateLifetimeTimestamp;
+
+ // A flag to indicate if the keepalive is active.
+ private boolean mKeepaliveActive = true;
+
+ /**
+ * Gets the lifetime stats for the keepalive, updated to timeNow, and then resets it.
+ *
+ * @param timeNow a timestamp obtained using Dependencies.getElapsedRealtime
+ */
+ public LifetimeStats getAndResetLifetimeStats(long timeNow) {
+ updateLifetimeStatsAndSetActive(timeNow, mKeepaliveActive);
+ // Get a snapshot of the stats
+ final LifetimeStats lifetimeStats = new LifetimeStats(mLifetimeMs, mActiveLifetimeMs);
+ // Reset the stats
+ resetLifetimeStats(timeNow);
+
+ return lifetimeStats;
+ }
+
+ public boolean isKeepaliveActive() {
+ return mKeepaliveActive;
+ }
+
+ KeepaliveStats(
+ int carrierId,
+ int transportTypes,
+ int intervalSeconds,
+ int appUid,
+ boolean isAutoKeepalive,
+ long timeNow) {
+ this.carrierId = carrierId;
+ this.transportTypes = transportTypes;
+ this.intervalMs = intervalSeconds * 1000;
+ this.appUid = appUid;
+ this.isAutoKeepalive = isAutoKeepalive;
+ mLastUpdateLifetimeTimestamp = timeNow;
+ }
+
+ /**
+ * Updates the lifetime metrics to the given time and sets the active state. This should be
+ * called whenever the active state of the keepalive changes.
+ *
+ * @param timeNow a timestamp obtained using Dependencies.getElapsedRealtime
+ */
+ public void updateLifetimeStatsAndSetActive(long timeNow, boolean keepaliveActive) {
+ final int durationIncrease = (int) (timeNow - mLastUpdateLifetimeTimestamp);
+ mLifetimeMs += durationIncrease;
+ if (mKeepaliveActive) mActiveLifetimeMs += durationIncrease;
+
+ mLastUpdateLifetimeTimestamp = timeNow;
+ mKeepaliveActive = keepaliveActive;
+ }
+
+ /**
+ * Resets the lifetime metrics but does not reset the active/stopped state of the keepalive.
+ * This also updates the time to timeNow, ensuring stats will start from this time.
+ *
+ * @param timeNow a timestamp obtained using Dependencies.getElapsedRealtime
+ */
+ public void resetLifetimeStats(long timeNow) {
+ mLifetimeMs = 0;
+ mActiveLifetimeMs = 0;
+ mLastUpdateLifetimeTimestamp = timeNow;
+ }
+ }
+
// List of duration stats metric where the index is the number of concurrent keepalives.
// Each DurationForNumOfKeepalive message stores a registered duration and an active duration.
// Registered duration is the total time spent with mNumRegisteredKeepalive == index.
@@ -51,6 +181,62 @@
private final List<DurationForNumOfKeepalive.Builder> mDurationPerNumOfKeepalive =
new ArrayList<>();
+ // Map of keepalives identified by the id from getKeepaliveId to their stats information.
+ private final SparseArray<KeepaliveStats> mKeepaliveStatsPerId = new SparseArray<>();
+
+ // Generate a unique integer using a given network's netId and the slot number.
+ // This is possible because netId is a 16 bit integer, so an integer with the first 16 bits as
+ // the netId and the last 16 bits as the slot number can be created. This allows slot numbers to
+ // be up to 2^16.
+ private int getKeepaliveId(@NonNull Network network, int slot) {
+ final int netId = network.getNetId();
+ if (netId < 0 || netId >= (1 << 16)) {
+ throw new IllegalArgumentException("Unexpected netId value: " + netId);
+ }
+ if (slot < 0 || slot >= (1 << 16)) {
+ throw new IllegalArgumentException("Unexpected slot value: " + slot);
+ }
+
+ return (netId << 16) + slot;
+ }
+
+ // Class to act as the key to aggregate the KeepaliveLifetimeForCarrier stats.
+ private static final class LifetimeKey {
+ public final int carrierId;
+ public final int transportTypes;
+ public final int intervalMs;
+
+ LifetimeKey(int carrierId, int transportTypes, int intervalMs) {
+ this.carrierId = carrierId;
+ this.transportTypes = transportTypes;
+ this.intervalMs = intervalMs;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final LifetimeKey that = (LifetimeKey) o;
+
+ return carrierId == that.carrierId && transportTypes == that.transportTypes
+ && intervalMs == that.intervalMs;
+ }
+
+ @Override
+ public int hashCode() {
+ return carrierId + 3 * transportTypes + 5 * intervalMs;
+ }
+ }
+
+ // Map to aggregate the KeepaliveLifetimeForCarrier stats using LifetimeKey as the key.
+ final Map<LifetimeKey, KeepaliveLifetimeForCarrier.Builder> mAggregateKeepaliveLifetime =
+ new HashMap<>();
+
+ private final Set<Integer> mAppUids = new HashSet<Integer>();
+ private int mNumKeepaliveRequests = 0;
+ private int mNumAutomaticKeepaliveRequests = 0;
+
private int mNumRegisteredKeepalive = 0;
private int mNumActiveKeepalive = 0;
@@ -60,23 +246,70 @@
/** Dependency class */
@VisibleForTesting
public static class Dependencies {
- // Returns a timestamp with the time base of SystemClock.uptimeMillis to keep durations
- // relative to start time and avoid timezone change.
- public long getUptimeMillis() {
- return SystemClock.uptimeMillis();
+ // Returns a timestamp with the time base of SystemClock.elapsedRealtime to keep durations
+ // relative to start time and avoid timezone change, including time spent in deep sleep.
+ public long getElapsedRealtime() {
+ return SystemClock.elapsedRealtime();
}
}
- public KeepaliveStatsTracker(@NonNull Handler handler) {
- this(handler, new Dependencies());
+ public KeepaliveStatsTracker(@NonNull Context context, @NonNull Handler handler) {
+ this(context, handler, new Dependencies());
}
@VisibleForTesting
- public KeepaliveStatsTracker(@NonNull Handler handler, @NonNull Dependencies dependencies) {
+ public KeepaliveStatsTracker(
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull Dependencies dependencies) {
+ Objects.requireNonNull(context);
mDependencies = Objects.requireNonNull(dependencies);
mConnectivityServiceHandler = Objects.requireNonNull(handler);
- mLastUpdateDurationsTimestamp = mDependencies.getUptimeMillis();
+ final SubscriptionManager subscriptionManager =
+ Objects.requireNonNull(context.getSystemService(SubscriptionManager.class));
+
+ mLastUpdateDurationsTimestamp = mDependencies.getElapsedRealtime();
+ context.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCachedDefaultSubscriptionId =
+ intent.getIntExtra(
+ SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+ },
+ new IntentFilter(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED),
+ /* broadcastPermission= */ null,
+ mConnectivityServiceHandler);
+
+ // The default constructor for OnSubscriptionsChangedListener will always implicitly grab
+ // the looper of the current thread. In the case the current thread does not have a looper,
+ // this will throw. Therefore, post a runnable that creates it there.
+ // When the callback is called on the BackgroundThread, post a message on the CS handler
+ // thread to update the caches, which can only be touched there.
+ BackgroundThread.getHandler().post(() ->
+ subscriptionManager.addOnSubscriptionsChangedListener(
+ r -> r.run(), new OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ final List<SubscriptionInfo> activeSubInfoList =
+ subscriptionManager.getActiveSubscriptionInfoList();
+ // A null subInfo list here indicates the current state is unknown
+ // but not necessarily empty, simply ignore it. Another call to the
+ // listener will be invoked in the future.
+ if (activeSubInfoList == null) return;
+ mConnectivityServiceHandler.post(() -> {
+ mCachedCarrierIdPerSubId.clear();
+
+ for (final SubscriptionInfo subInfo : activeSubInfoList) {
+ mCachedCarrierIdPerSubId.put(subInfo.getSubscriptionId(),
+ subInfo.getCarrierId());
+ }
+ });
+ }
+ }));
}
/** Ensures the list of duration metrics is large enough for number of registered keepalives. */
@@ -106,7 +339,7 @@
* change to mNumRegisteredKeepalive or mNumActiveKeepalive to keep the duration metrics
* correct.
*
- * @param timeNow a timestamp obtained using Dependencies.getUptimeMillis
+ * @param timeNow a timestamp obtained using Dependencies.getElapsedRealtime
*/
private void updateDurationsPerNumOfKeepalive(long timeNow) {
if (mDurationPerNumOfKeepalive.size() < mNumRegisteredKeepalive) {
@@ -132,55 +365,201 @@
mLastUpdateDurationsTimestamp = timeNow;
}
- /** Inform the KeepaliveStatsTracker a keepalive has just started and is active. */
- public void onStartKeepalive() {
- ensureRunningOnHandlerThread();
+ // TODO: Move this function to frameworks/libs/net/.../NetworkCapabilitiesUtils.java
+ private static int getSubId(@NonNull NetworkCapabilities nc, int defaultSubId) {
+ if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ final NetworkSpecifier networkSpecifier = nc.getNetworkSpecifier();
+ if (networkSpecifier instanceof TelephonyNetworkSpecifier) {
+ return ((TelephonyNetworkSpecifier) networkSpecifier).getSubscriptionId();
+ }
+ // Use the default subscriptionId.
+ return defaultSubId;
+ }
+ if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ final TransportInfo info = nc.getTransportInfo();
+ if (info instanceof WifiInfo) {
+ return ((WifiInfo) info).getSubscriptionId();
+ }
+ }
- final long timeNow = mDependencies.getUptimeMillis();
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ private int getCarrierId(@NonNull NetworkCapabilities networkCapabilities) {
+ // Try to get the correct subscription id.
+ final int subId = getSubId(networkCapabilities, mCachedDefaultSubscriptionId);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return TelephonyManager.UNKNOWN_CARRIER_ID;
+ }
+ return mCachedCarrierIdPerSubId.get(subId, TelephonyManager.UNKNOWN_CARRIER_ID);
+ }
+
+ private int getTransportTypes(@NonNull NetworkCapabilities networkCapabilities) {
+ // Transport types are internally packed as bits starting from bit 0. Casting to int works
+ // fine since for now and the foreseeable future, there will be less than 32 transports.
+ return (int) networkCapabilities.getTransportTypesInternal();
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just started and is active. */
+ public void onStartKeepalive(
+ @NonNull Network network,
+ int slot,
+ @NonNull NetworkCapabilities nc,
+ int intervalSeconds,
+ int appUid,
+ boolean isAutoKeepalive) {
+ ensureRunningOnHandlerThread();
+ final int keepaliveId = getKeepaliveId(network, slot);
+ if (mKeepaliveStatsPerId.contains(keepaliveId)) {
+ throw new IllegalArgumentException(
+ "Attempt to start keepalive stats on a known network, slot pair");
+ }
+
+ mNumKeepaliveRequests++;
+ if (isAutoKeepalive) mNumAutomaticKeepaliveRequests++;
+ mAppUids.add(appUid);
+
+ final long timeNow = mDependencies.getElapsedRealtime();
updateDurationsPerNumOfKeepalive(timeNow);
mNumRegisteredKeepalive++;
mNumActiveKeepalive++;
+
+ final KeepaliveStats newKeepaliveStats =
+ new KeepaliveStats(
+ getCarrierId(nc),
+ getTransportTypes(nc),
+ intervalSeconds,
+ appUid,
+ isAutoKeepalive,
+ timeNow);
+
+ mKeepaliveStatsPerId.put(keepaliveId, newKeepaliveStats);
+ }
+
+ /**
+ * Inform the KeepaliveStatsTracker that the keepalive with the given network, slot pair has
+ * updated its active state to keepaliveActive.
+ *
+ * @return the KeepaliveStats associated with the network, slot pair or null if it is unknown.
+ */
+ private @NonNull KeepaliveStats onKeepaliveActive(
+ @NonNull Network network, int slot, boolean keepaliveActive) {
+ final long timeNow = mDependencies.getElapsedRealtime();
+ return onKeepaliveActive(network, slot, keepaliveActive, timeNow);
+ }
+
+ /**
+ * Inform the KeepaliveStatsTracker that the keepalive with the given network, slot pair has
+ * updated its active state to keepaliveActive.
+ *
+ * @param network the network of the keepalive
+ * @param slot the slot number of the keepalive
+ * @param keepaliveActive the new active state of the keepalive
+ * @param timeNow a timestamp obtained using Dependencies.getElapsedRealtime
+ * @return the KeepaliveStats associated with the network, slot pair or null if it is unknown.
+ */
+ private @NonNull KeepaliveStats onKeepaliveActive(
+ @NonNull Network network, int slot, boolean keepaliveActive, long timeNow) {
+ ensureRunningOnHandlerThread();
+
+ final int keepaliveId = getKeepaliveId(network, slot);
+ if (!mKeepaliveStatsPerId.contains(keepaliveId)) {
+ throw new IllegalArgumentException(
+ "Attempt to set active keepalive on an unknown network, slot pair");
+ }
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ final KeepaliveStats keepaliveStats = mKeepaliveStatsPerId.get(keepaliveId);
+ if (keepaliveActive != keepaliveStats.isKeepaliveActive()) {
+ mNumActiveKeepalive += keepaliveActive ? 1 : -1;
+ }
+
+ keepaliveStats.updateLifetimeStatsAndSetActive(timeNow, keepaliveActive);
+ return keepaliveStats;
}
/** Inform the KeepaliveStatsTracker a keepalive has just been paused. */
- public void onPauseKeepalive() {
- ensureRunningOnHandlerThread();
-
- final long timeNow = mDependencies.getUptimeMillis();
- updateDurationsPerNumOfKeepalive(timeNow);
-
- mNumActiveKeepalive--;
+ public void onPauseKeepalive(@NonNull Network network, int slot) {
+ onKeepaliveActive(network, slot, /* keepaliveActive= */ false);
}
/** Inform the KeepaliveStatsTracker a keepalive has just been resumed. */
- public void onResumeKeepalive() {
- ensureRunningOnHandlerThread();
-
- final long timeNow = mDependencies.getUptimeMillis();
- updateDurationsPerNumOfKeepalive(timeNow);
-
- mNumActiveKeepalive++;
+ public void onResumeKeepalive(@NonNull Network network, int slot) {
+ onKeepaliveActive(network, slot, /* keepaliveActive= */ true);
}
/** Inform the KeepaliveStatsTracker a keepalive has just been stopped. */
- public void onStopKeepalive(boolean wasActive) {
- ensureRunningOnHandlerThread();
+ public void onStopKeepalive(@NonNull Network network, int slot) {
+ final int keepaliveId = getKeepaliveId(network, slot);
+ final long timeNow = mDependencies.getElapsedRealtime();
- final long timeNow = mDependencies.getUptimeMillis();
- updateDurationsPerNumOfKeepalive(timeNow);
+ final KeepaliveStats keepaliveStats =
+ onKeepaliveActive(network, slot, /* keepaliveActive= */ false, timeNow);
mNumRegisteredKeepalive--;
- if (wasActive) mNumActiveKeepalive--;
+
+ // add to the aggregate since it will be removed.
+ addToAggregateKeepaliveLifetime(keepaliveStats, timeNow);
+ // free up the slot.
+ mKeepaliveStatsPerId.remove(keepaliveId);
+ }
+
+ /**
+ * Updates and adds the lifetime metric of keepaliveStats to the aggregate.
+ *
+ * @param keepaliveStats the stats to add to the aggregate
+ * @param timeNow a timestamp obtained using Dependencies.getElapsedRealtime
+ */
+ private void addToAggregateKeepaliveLifetime(
+ @NonNull KeepaliveStats keepaliveStats, long timeNow) {
+
+ final KeepaliveStats.LifetimeStats lifetimeStats =
+ keepaliveStats.getAndResetLifetimeStats(timeNow);
+
+ final LifetimeKey key =
+ new LifetimeKey(
+ keepaliveStats.carrierId,
+ keepaliveStats.transportTypes,
+ keepaliveStats.intervalMs);
+
+ KeepaliveLifetimeForCarrier.Builder keepaliveLifetimeForCarrier =
+ mAggregateKeepaliveLifetime.get(key);
+
+ if (keepaliveLifetimeForCarrier == null) {
+ keepaliveLifetimeForCarrier =
+ KeepaliveLifetimeForCarrier.newBuilder()
+ .setCarrierId(keepaliveStats.carrierId)
+ .setTransportTypes(keepaliveStats.transportTypes)
+ .setIntervalsMsec(keepaliveStats.intervalMs);
+ mAggregateKeepaliveLifetime.put(key, keepaliveLifetimeForCarrier);
+ }
+
+ keepaliveLifetimeForCarrier.setLifetimeMsec(
+ keepaliveLifetimeForCarrier.getLifetimeMsec() + lifetimeStats.lifetimeMs);
+ keepaliveLifetimeForCarrier.setActiveLifetimeMsec(
+ keepaliveLifetimeForCarrier.getActiveLifetimeMsec()
+ + lifetimeStats.activeLifetimeMs);
}
/**
* Builds and returns DailykeepaliveInfoReported proto.
+ *
+ * @return the DailykeepaliveInfoReported proto that was built.
*/
- public DailykeepaliveInfoReported buildKeepaliveMetrics() {
+ @VisibleForTesting
+ public @NonNull DailykeepaliveInfoReported buildKeepaliveMetrics() {
ensureRunningOnHandlerThread();
+ final long timeNow = mDependencies.getElapsedRealtime();
+ return buildKeepaliveMetrics(timeNow);
+ }
- final long timeNow = mDependencies.getUptimeMillis();
+ /**
+ * Updates the metrics to timeNow and builds and returns DailykeepaliveInfoReported proto.
+ *
+ * @param timeNow a timestamp obtained using Dependencies.getElapsedRealtime
+ */
+ private @NonNull DailykeepaliveInfoReported buildKeepaliveMetrics(long timeNow) {
updateDurationsPerNumOfKeepalive(timeNow);
final DurationPerNumOfKeepalive.Builder durationPerNumOfKeepalive =
@@ -191,21 +570,82 @@
durationPerNumOfKeepalive.addDurationForNumOfKeepalive(
durationForNumOfKeepalive));
+ final KeepaliveLifetimePerCarrier.Builder keepaliveLifetimePerCarrier =
+ KeepaliveLifetimePerCarrier.newBuilder();
+
+ for (int i = 0; i < mKeepaliveStatsPerId.size(); i++) {
+ final KeepaliveStats keepaliveStats = mKeepaliveStatsPerId.valueAt(i);
+ addToAggregateKeepaliveLifetime(keepaliveStats, timeNow);
+ }
+
+ // Fill keepalive carrier stats to the proto
+ mAggregateKeepaliveLifetime
+ .values()
+ .forEach(
+ keepaliveLifetimeForCarrier ->
+ keepaliveLifetimePerCarrier.addKeepaliveLifetimeForCarrier(
+ keepaliveLifetimeForCarrier));
+
final DailykeepaliveInfoReported.Builder dailyKeepaliveInfoReported =
DailykeepaliveInfoReported.newBuilder();
- // TODO(b/273451360): fill all the other values and write to ConnectivityStatsLog.
dailyKeepaliveInfoReported.setDurationPerNumOfKeepalive(durationPerNumOfKeepalive);
+ dailyKeepaliveInfoReported.setKeepaliveLifetimePerCarrier(keepaliveLifetimePerCarrier);
+ dailyKeepaliveInfoReported.setKeepaliveRequests(mNumKeepaliveRequests);
+ dailyKeepaliveInfoReported.setAutomaticKeepaliveRequests(mNumAutomaticKeepaliveRequests);
+ dailyKeepaliveInfoReported.setDistinctUserCount(mAppUids.size());
+ dailyKeepaliveInfoReported.addAllUid(mAppUids);
return dailyKeepaliveInfoReported.build();
}
- /** Resets the stored metrics but maintains the state of keepalives */
- public void resetMetrics() {
+ /**
+ * Builds and resets the stored metrics. Similar to buildKeepaliveMetrics but also resets the
+ * metrics while maintaining the state of the keepalives.
+ *
+ * @return the DailykeepaliveInfoReported proto that was built.
+ */
+ @VisibleForTesting
+ public @NonNull DailykeepaliveInfoReported buildAndResetMetrics() {
ensureRunningOnHandlerThread();
+ final long timeNow = mDependencies.getElapsedRealtime();
+
+ final DailykeepaliveInfoReported metrics = buildKeepaliveMetrics(timeNow);
mDurationPerNumOfKeepalive.clear();
+ mAggregateKeepaliveLifetime.clear();
+ mAppUids.clear();
+ mNumKeepaliveRequests = 0;
+ mNumAutomaticKeepaliveRequests = 0;
+
+ // Update the metrics with the existing keepalives.
ensureDurationPerNumOfKeepaliveSize();
+
+ mAggregateKeepaliveLifetime.clear();
+ // Reset the stats for existing keepalives
+ for (int i = 0; i < mKeepaliveStatsPerId.size(); i++) {
+ final KeepaliveStats keepaliveStats = mKeepaliveStatsPerId.valueAt(i);
+ keepaliveStats.resetLifetimeStats(timeNow);
+ mAppUids.add(keepaliveStats.appUid);
+ mNumKeepaliveRequests++;
+ if (keepaliveStats.isAutoKeepalive) mNumAutomaticKeepaliveRequests++;
+ }
+
+ return metrics;
+ }
+
+ /** Writes the stored metrics to ConnectivityStatsLog and resets. */
+ public void writeAndResetMetrics() {
+ ensureRunningOnHandlerThread();
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported = buildAndResetMetrics();
+ ConnectivityStatsLog.write(
+ ConnectivityStatsLog.DAILY_KEEPALIVE_INFO_REPORTED,
+ dailyKeepaliveInfoReported.getDurationPerNumOfKeepalive().toByteArray(),
+ dailyKeepaliveInfoReported.getKeepaliveLifetimePerCarrier().toByteArray(),
+ dailyKeepaliveInfoReported.getKeepaliveRequests(),
+ dailyKeepaliveInfoReported.getAutomaticKeepaliveRequests(),
+ dailyKeepaliveInfoReported.getDistinctUserCount(),
+ CollectionUtils.toIntArray(dailyKeepaliveInfoReported.getUidList()));
}
private void ensureRunningOnHandlerThread() {
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 1fd8a62..b4f74d5 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -271,6 +271,10 @@
return mInterval;
}
+ public int getUid() {
+ return mUid;
+ }
+
private int checkNetworkConnected() {
if (!mNai.networkInfo.isConnectedOrConnecting()) {
return ERROR_INVALID_NETWORK;
diff --git a/tests/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
index 36b9a91..1f04fb8 100644
--- a/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
+++ b/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
@@ -46,6 +46,8 @@
private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
private val TEST_DST_ADDRV4 = "198.168.0.1".address()
private val TEST_ADDRV6 = "2001:db8::1".address()
+ private val TEST_ADDRV4MAPPEDV6 = "::ffff:1.2.3.4".address()
+ private val TEST_ADDRV4 = "1.2.3.4".address()
private fun String.address() = InetAddresses.parseNumericAddress(this)
private fun nattKeepalivePacket(
@@ -83,6 +85,28 @@
}
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ fun testConstructor_afterR() {
+ // v4 mapped v6 will be translated to a v4 address.
+ assertFailsWith<InvalidPacketException> {
+ nattKeepalivePacket(srcAddress = TEST_ADDRV6, dstAddress = TEST_ADDRV4MAPPEDV6)
+ }
+ assertFailsWith<InvalidPacketException> {
+ nattKeepalivePacket(srcAddress = TEST_ADDRV4MAPPEDV6, dstAddress = TEST_ADDRV6)
+ }
+
+ // Both src and dst address will be v4 after translation, so it won't cause exception.
+ val packet1 = nattKeepalivePacket(
+ dstAddress = TEST_ADDRV4MAPPEDV6, srcAddress = TEST_ADDRV4MAPPEDV6)
+ assertEquals(TEST_ADDRV4, packet1.srcAddress)
+ assertEquals(TEST_ADDRV4, packet1.dstAddress)
+
+ // Packet with v6 src and v6 dst address is valid.
+ val packet2 = nattKeepalivePacket(srcAddress = TEST_ADDRV6, dstAddress = TEST_ADDRV6)
+ assertEquals(TEST_ADDRV6, packet2.srcAddress)
+ assertEquals(TEST_ADDRV6, packet2.dstAddress)
+ }
+
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testParcel() {
assertParcelingIsLossless(nattKeepalivePacket())
diff --git a/tests/cts/net/jni/NativeMultinetworkJni.cpp b/tests/cts/net/jni/NativeMultinetworkJni.cpp
index 6610d10..f2214a3 100644
--- a/tests/cts/net/jni/NativeMultinetworkJni.cpp
+++ b/tests/cts/net/jni/NativeMultinetworkJni.cpp
@@ -42,11 +42,14 @@
// Since the tests in this file commonly pass expression statements as parameters to these macros,
// get the returned value of the statements to avoid statement double-called.
+// By checking ExceptionCheck(), these macros don't throw another exception if an exception has
+// been thrown, because ART's JNI disallows to throw another exception while an exception is
+// pending (See CheckThread in check_jni.cc).
#define EXPECT_GE(env, actual_stmt, expected_stmt, msg) \
do { \
const auto expected = (expected_stmt); \
const auto actual = (actual_stmt); \
- if (actual < expected) { \
+ if (actual < expected && !env->ExceptionCheck()) { \
jniThrowExceptionFmt(env, "java/lang/AssertionError", \
"%s:%d: %s EXPECT_GE: expected %d, got %d", \
__FILE__, __LINE__, msg, expected, actual); \
@@ -57,7 +60,7 @@
do { \
const auto expected = (expected_stmt); \
const auto actual = (actual_stmt); \
- if (actual <= expected) { \
+ if (actual <= expected && !env->ExceptionCheck()) { \
jniThrowExceptionFmt(env, "java/lang/AssertionError", \
"%s:%d: %s EXPECT_GT: expected %d, got %d", \
__FILE__, __LINE__, msg, expected, actual); \
@@ -68,7 +71,7 @@
do { \
const auto expected = (expected_stmt); \
const auto actual = (actual_stmt); \
- if (actual != expected) { \
+ if (actual != expected && !env->ExceptionCheck()) { \
jniThrowExceptionFmt(env, "java/lang/AssertionError", \
"%s:%d: %s EXPECT_EQ: expected %d, got %d", \
__FILE__, __LINE__, msg, expected, actual); \
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index ce789fc..21f1358 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -86,7 +86,7 @@
private static final String FEATURE_IPSEC_TUNNEL_MIGRATION =
"android.software.ipsec_tunnel_migration";
- private static final int SOCKET_TIMEOUT_MS = 2000;
+ private static final int SOCKET_TIMEOUT_MS = 10_000;
private static final int PRIVATE_DNS_PROBE_MS = 1_000;
private static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 10_000;
diff --git a/tests/cts/netpermission/internetpermission/Android.bp b/tests/cts/netpermission/internetpermission/Android.bp
index 37ad7cb..5314396 100644
--- a/tests/cts/netpermission/internetpermission/Android.bp
+++ b/tests/cts/netpermission/internetpermission/Android.bp
@@ -29,5 +29,5 @@
"cts",
"general-tests",
],
-
+ host_required: ["net-tests-utils-host-common"],
}
diff --git a/tests/cts/netpermission/internetpermission/AndroidTest.xml b/tests/cts/netpermission/internetpermission/AndroidTest.xml
index 3b23e72..e326844 100644
--- a/tests/cts/netpermission/internetpermission/AndroidTest.xml
+++ b/tests/cts/netpermission/internetpermission/AndroidTest.xml
@@ -24,6 +24,8 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsNetTestCasesInternetPermission.apk" />
</target_preparer>
+ <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.networkpermission.internetpermission.cts" />
<option name="runtime-hint" value="10s" />
diff --git a/tests/cts/netpermission/updatestatspermission/Android.bp b/tests/cts/netpermission/updatestatspermission/Android.bp
index 7a24886..40474db 100644
--- a/tests/cts/netpermission/updatestatspermission/Android.bp
+++ b/tests/cts/netpermission/updatestatspermission/Android.bp
@@ -29,5 +29,5 @@
"cts",
"general-tests",
],
-
+ host_required: ["net-tests-utils-host-common"],
}
diff --git a/tests/cts/netpermission/updatestatspermission/AndroidTest.xml b/tests/cts/netpermission/updatestatspermission/AndroidTest.xml
index c47cad9..a1019fa 100644
--- a/tests/cts/netpermission/updatestatspermission/AndroidTest.xml
+++ b/tests/cts/netpermission/updatestatspermission/AndroidTest.xml
@@ -24,6 +24,8 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsNetTestCasesUpdateStatsPermission.apk" />
</target_preparer>
+ <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.networkpermission.updatestatspermission.cts" />
<option name="runtime-hint" value="10s" />
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 43c6225..8f3e097 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -355,6 +355,7 @@
import android.provider.Settings;
import android.security.Credentials;
import android.system.Os;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.NrQosSessionAttributes;
@@ -616,6 +617,7 @@
@Mock BroadcastOptionsShim mBroadcastOptionsShim;
@Mock ActivityManager mActivityManager;
@Mock DestroySocketsWrapper mDestroySocketsWrapper;
+ @Mock SubscriptionManager mSubscriptionManager;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -740,6 +742,7 @@
if (Context.PAC_PROXY_SERVICE.equals(name)) return mPacProxyManager;
if (Context.TETHERING_SERVICE.equals(name)) return mTetheringManager;
if (Context.ACTIVITY_SERVICE.equals(name)) return mActivityManager;
+ if (Context.TELEPHONY_SUBSCRIPTION_SERVICE.equals(name)) return mSubscriptionManager;
return super.getSystemService(name);
}
@@ -6853,10 +6856,6 @@
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
- // NAT-T is only supported for IPv4.
- ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv6);
- callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
-
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
@@ -7007,13 +7006,6 @@
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
}
- // NAT-T is only supported for IPv4.
- try (SocketKeepalive ka = mCm.createSocketKeepalive(
- myNet, testSocket, myIPv6, dstIPv6, executor, callback)) {
- ka.start(validKaInterval);
- callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
- }
-
// Basic check before testing started keepalive.
try (SocketKeepalive ka = mCm.createSocketKeepalive(
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
diff --git a/tests/unit/java/com/android/server/VpnManagerServiceTest.java b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
index deb56ef..bf23cd1 100644
--- a/tests/unit/java/com/android/server/VpnManagerServiceTest.java
+++ b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
@@ -75,12 +75,15 @@
@IgnoreUpTo(R) // VpnManagerService is not available before R
@SmallTest
public class VpnManagerServiceTest extends VpnTestBase {
+ private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER";
+
@Rule
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
private static final int TIMEOUT_MS = 2_000;
@Mock Context mContext;
+ @Mock Context mContextWithoutAttributionTag;
@Mock Context mSystemContext;
@Mock Context mUserAllContext;
private HandlerThread mHandlerThread;
@@ -144,6 +147,13 @@
mHandlerThread = new HandlerThread("TestVpnManagerService");
mDeps = new VpnManagerServiceDependencies();
+
+ // The attribution tag is a dependency for IKE library to collect VPN metrics correctly
+ // and thus should not be changed without updating the IKE code.
+ doReturn(mContext)
+ .when(mContextWithoutAttributionTag)
+ .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
+
doReturn(mUserAllContext).when(mContext).createContextAsUser(UserHandle.ALL, 0);
doReturn(mSystemContext).when(mContext).createContextAsUser(UserHandle.SYSTEM, 0);
doReturn(mPackageManager).when(mContext).getPackageManager();
@@ -153,7 +163,7 @@
mockService(mContext, UserManager.class, Context.USER_SERVICE, mUserManager);
doReturn(SYSTEM_USER).when(mUserManager).getUserInfo(eq(SYSTEM_USER_ID));
- mService = new VpnManagerService(mContext, mDeps);
+ mService = new VpnManagerService(mContextWithoutAttributionTag, mDeps);
mService.systemReady();
final ArgumentCaptor<BroadcastReceiver> intentReceiverCaptor =
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index db65c2b..8232658 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -21,6 +21,7 @@
import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static com.android.server.connectivity.AutomaticOnOffKeepaliveTracker.METRICS_COLLECTION_DURATION_MS;
import static com.android.testutils.HandlerUtils.visibleOnHandlerThread;
import static org.junit.Assert.assertEquals;
@@ -36,11 +37,13 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.ignoreStubs;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -67,6 +70,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.telephony.SubscriptionManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -121,7 +125,9 @@
@Mock Context mCtx;
@Mock AlarmManager mAlarmManager;
@Mock NetworkAgentInfo mNai;
+ @Mock SubscriptionManager mSubscriptionManager;
+ KeepaliveStatsTracker mKeepaliveStatsTracker;
TestKeepaliveTracker mKeepaliveTracker;
AOOTestHandler mTestHandler;
TestTcpKeepaliveController mTcpController;
@@ -298,10 +304,22 @@
}
}
+ private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+ doReturn(serviceName).when(mCtx).getSystemServiceName(serviceClass);
+ doReturn(service).when(mCtx).getSystemService(serviceName);
+ if (mCtx.getSystemService(serviceClass) == null) {
+ // Test is using mockito-extended
+ doCallRealMethod().when(mCtx).getSystemService(serviceClass);
+ }
+ }
+
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ mockService(Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class,
+ mSubscriptionManager);
+
mNai.networkCapabilities =
new NetworkCapabilities.Builder().addTransportType(TRANSPORT_CELLULAR).build();
mNai.networkInfo = new NetworkInfo(TYPE_MOBILE, 0 /* subtype */, "LTE", "LTE");
@@ -329,8 +347,14 @@
mTestHandler = new AOOTestHandler(mHandlerThread.getLooper());
mTcpController = new TestTcpKeepaliveController(mTestHandler);
mKeepaliveTracker = new TestKeepaliveTracker(mCtx, mTestHandler, mTcpController);
+ mKeepaliveStatsTracker = spy(new KeepaliveStatsTracker(mCtx, mTestHandler));
doReturn(mKeepaliveTracker).when(mDependencies).newKeepaliveTracker(mCtx, mTestHandler);
+ doReturn(mKeepaliveStatsTracker)
+ .when(mDependencies)
+ .newKeepaliveStatsTracker(mCtx, mTestHandler);
+
doReturn(true).when(mDependencies).isFeatureEnabled(any(), anyBoolean());
+ doReturn(0L).when(mDependencies).getElapsedRealtime();
mAOOKeepaliveTracker =
new AutomaticOnOffKeepaliveTracker(mCtx, mTestHandler, mDependencies);
}
@@ -484,6 +508,30 @@
assertEquals(testInfo.underpinnedNetwork, mTestHandler.mLastAutoKi.getUnderpinnedNetwork());
}
+ @Test
+ public void testAlarm_writeMetrics() throws Exception {
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> listenerCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+
+ // First AlarmManager.set call from the constructor.
+ verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(METRICS_COLLECTION_DURATION_MS), any() /* tag */, listenerCaptor.capture(),
+ eq(mTestHandler));
+
+ final AlarmManager.OnAlarmListener listener = listenerCaptor.getValue();
+
+ doReturn(METRICS_COLLECTION_DURATION_MS).when(mDependencies).getElapsedRealtime();
+ // For realism, the listener should be posted on the handler
+ mTestHandler.post(() -> listener.onAlarm());
+ HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
+
+ verify(mKeepaliveStatsTracker).writeAndResetMetrics();
+ // Alarm is rescheduled.
+ verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(METRICS_COLLECTION_DURATION_MS * 2),
+ any() /* tag */, listenerCaptor.capture(), eq(mTestHandler));
+ }
+
private void setupResponseWithSocketExisting() throws Exception {
final ByteBuffer tcpBufferV6 = getByteBuffer(TEST_RESPONSE_BYTES);
final ByteBuffer tcpBufferV4 = getByteBuffer(TEST_RESPONSE_BYTES);
@@ -772,41 +820,36 @@
clearInvocations(mNai);
// Start the second keepalive while the first is paused.
- final TestKeepaliveInfo testInfo2 = doStartNattKeepalive();
- // The slot used is TEST_SLOT since it is now a free slot.
- checkAndProcessKeepaliveStart(TEST_SLOT, testInfo2.kpd);
- verify(testInfo2.socketKeepaliveCallback).onStarted();
- assertNotNull(getAutoKiForBinder(testInfo2.binder));
+ // TODO: Uncomment the following test after fixing b/283886067. Currently this attempts to
+ // start the keepalive on TEST_SLOT and this throws in the handler thread.
+ // final TestKeepaliveInfo testInfo2 = doStartNattKeepalive();
+ // // The slot used is TEST_SLOT + 1 since TEST_SLOT is being taken by the paused keepalive.
+ // checkAndProcessKeepaliveStart(TEST_SLOT + 1, testInfo2.kpd);
+ // verify(testInfo2.socketKeepaliveCallback).onStarted();
+ // assertNotNull(getAutoKiForBinder(testInfo2.binder));
- clearInvocations(mNai);
- doResumeKeepalive(autoKi1);
- // The next free slot is TEST_SLOT + 1.
- checkAndProcessKeepaliveStart(TEST_SLOT + 1, testInfo1.kpd);
- verify(testInfo1.socketKeepaliveCallback).onResumed();
+ // clearInvocations(mNai);
+ // doResumeKeepalive(autoKi1);
+ // // Resume on TEST_SLOT.
+ // checkAndProcessKeepaliveStart(TEST_SLOT, testInfo1.kpd);
+ // verify(testInfo1.socketKeepaliveCallback).onResumed();
- clearInvocations(mNai);
- doStopKeepalive(autoKi1);
- // TODO: The slot should be consistent with the checkAndProcessKeepaliveStart directly above
- checkAndProcessKeepaliveStop(TEST_SLOT);
- // TODO: onStopped should only be called on the first keepalive callback.
- verify(testInfo1.socketKeepaliveCallback, never()).onStopped();
- verify(testInfo2.socketKeepaliveCallback).onStopped();
- assertNull(getAutoKiForBinder(testInfo1.binder));
+ // clearInvocations(mNai);
+ // doStopKeepalive(autoKi1);
+ // checkAndProcessKeepaliveStop(TEST_SLOT);
+ // verify(testInfo1.socketKeepaliveCallback).onStopped();
+ // verify(testInfo2.socketKeepaliveCallback, never()).onStopped();
+ // assertNull(getAutoKiForBinder(testInfo1.binder));
- clearInvocations(mNai);
- assertNotNull(getAutoKiForBinder(testInfo2.binder));
- doStopKeepalive(getAutoKiForBinder(testInfo2.binder));
- // This slot should be consistent with its corresponding checkAndProcessKeepaliveStart.
- // TODO: checkAndProcessKeepaliveStop should be called instead but the keepalive is
- // unexpectedly already stopped above.
- verify(mNai, never()).onStopSocketKeepalive(TEST_SLOT);
- verify(mNai, never()).onRemoveKeepalivePacketFilter(TEST_SLOT);
+ // clearInvocations(mNai);
+ // assertNotNull(getAutoKiForBinder(testInfo2.binder));
+ // doStopKeepalive(getAutoKiForBinder(testInfo2.binder));
+ // checkAndProcessKeepaliveStop(TEST_SLOT + 1);
+ // verify(testInfo2.socketKeepaliveCallback).onStopped();
+ // assertNull(getAutoKiForBinder(testInfo2.binder));
- verify(testInfo2.socketKeepaliveCallback).onStopped();
- assertNull(getAutoKiForBinder(testInfo2.binder));
-
- verifyNoMoreInteractions(ignoreStubs(testInfo1.socketKeepaliveCallback));
- verifyNoMoreInteractions(ignoreStubs(testInfo2.socketKeepaliveCallback));
+ // verifyNoMoreInteractions(ignoreStubs(testInfo1.socketKeepaliveCallback));
+ // verifyNoMoreInteractions(ignoreStubs(testInfo2.socketKeepaliveCallback));
}
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
index 2e9bf26..0d2e540 100644
--- a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
@@ -16,112 +16,298 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import static com.android.testutils.HandlerUtils.visibleOnHandlerThread;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import com.android.metrics.DailykeepaliveInfoReported;
import com.android.metrics.DurationForNumOfKeepalive;
import com.android.metrics.DurationPerNumOfKeepalive;
+import com.android.metrics.KeepaliveLifetimeForCarrier;
+import com.android.metrics.KeepaliveLifetimePerCarrier;
+import com.android.modules.utils.BackgroundThread;
+import com.android.net.module.util.CollectionUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.HandlerUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
public class KeepaliveStatsTrackerTest {
+ private static final int TIMEOUT_MS = 30_000;
+
+ private static final int TEST_SLOT = 1;
+ private static final int TEST_SLOT2 = 2;
+ private static final int TEST_KEEPALIVE_INTERVAL_SEC = 10;
+ private static final int TEST_KEEPALIVE_INTERVAL2_SEC = 20;
+ private static final int TEST_SUB_ID_1 = 1;
+ private static final int TEST_SUB_ID_2 = 2;
+ private static final int TEST_CARRIER_ID_1 = 135;
+ private static final int TEST_CARRIER_ID_2 = 246;
+ private static final Network TEST_NETWORK = new Network(123);
+ private static final NetworkCapabilities TEST_NETWORK_CAPABILITIES =
+ buildCellNetworkCapabilitiesWithSubId(TEST_SUB_ID_1);
+ private static final NetworkCapabilities TEST_NETWORK_CAPABILITIES_2 =
+ buildCellNetworkCapabilitiesWithSubId(TEST_SUB_ID_2);
+ private static final int TEST_UID = 1234;
+
+ private static NetworkCapabilities buildCellNetworkCapabilitiesWithSubId(int subId) {
+ final TelephonyNetworkSpecifier telephonyNetworkSpecifier =
+ new TelephonyNetworkSpecifier.Builder().setSubscriptionId(subId).build();
+ return new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(telephonyNetworkSpecifier)
+ .build();
+ }
+
private HandlerThread mHandlerThread;
private Handler mTestHandler;
private KeepaliveStatsTracker mKeepaliveStatsTracker;
+ @Mock private Context mContext;
@Mock private KeepaliveStatsTracker.Dependencies mDependencies;
+ @Mock private SubscriptionManager mSubscriptionManager;
+
+ private void triggerBroadcastDefaultSubId(int subId) {
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContext).registerReceiver(receiverCaptor.capture(), /* filter= */ any(),
+ /* broadcastPermission= */ any(), eq(mTestHandler));
+ final Intent intent =
+ new Intent(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+
+ receiverCaptor.getValue().onReceive(mContext, intent);
+ }
+
+ private OnSubscriptionsChangedListener getOnSubscriptionsChangedListener() {
+ final ArgumentCaptor<OnSubscriptionsChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class);
+ verify(mSubscriptionManager)
+ .addOnSubscriptionsChangedListener(any(), listenerCaptor.capture());
+ return listenerCaptor.getValue();
+ }
+
+ private static final class KeepaliveCarrierStats {
+ public final int carrierId;
+ public final int transportTypes;
+ public final int intervalMs;
+ public final int lifetimeMs;
+ public final int activeLifetimeMs;
+
+ KeepaliveCarrierStats(
+ int carrierId,
+ int transportTypes,
+ int intervalMs,
+ int lifetimeMs,
+ int activeLifetimeMs) {
+ this.carrierId = carrierId;
+ this.transportTypes = transportTypes;
+ this.intervalMs = intervalMs;
+ this.lifetimeMs = lifetimeMs;
+ this.activeLifetimeMs = activeLifetimeMs;
+ }
+
+ // Equals method on only the key, (carrierId, tranportTypes, intervalMs)
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final KeepaliveCarrierStats that = (KeepaliveCarrierStats) o;
+
+ return carrierId == that.carrierId && transportTypes == that.transportTypes
+ && intervalMs == that.intervalMs;
+ }
+
+ @Override
+ public int hashCode() {
+ return carrierId + 3 * transportTypes + 5 * intervalMs;
+ }
+ }
+
+ // Use the default test carrier id, transportType and keepalive interval.
+ private KeepaliveCarrierStats getDefaultCarrierStats(int lifetimeMs, int activeLifetimeMs) {
+ return new KeepaliveCarrierStats(
+ TEST_CARRIER_ID_1,
+ /* transportTypes= */ (1 << TRANSPORT_CELLULAR),
+ TEST_KEEPALIVE_INTERVAL_SEC * 1000,
+ lifetimeMs,
+ activeLifetimeMs);
+ }
+
+ private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+ doReturn(serviceName).when(mContext).getSystemServiceName(serviceClass);
+ doReturn(service).when(mContext).getSystemService(serviceName);
+ if (mContext.getSystemService(serviceClass) == null) {
+ // Test is using mockito-extended
+ doCallRealMethod().when(mContext).getSystemService(serviceClass);
+ }
+ }
+
+ private SubscriptionInfo makeSubInfoMock(int subId, int carrierId) {
+ final SubscriptionInfo subInfo = mock(SubscriptionInfo.class);
+ doReturn(subId).when(subInfo).getSubscriptionId();
+ doReturn(carrierId).when(subInfo).getCarrierId();
+ return subInfo;
+ }
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mockService(Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class,
+ mSubscriptionManager);
+
+ final SubscriptionInfo subInfo1 = makeSubInfoMock(TEST_SUB_ID_1, TEST_CARRIER_ID_1);
+ final SubscriptionInfo subInfo2 = makeSubInfoMock(TEST_SUB_ID_2, TEST_CARRIER_ID_2);
+
+ doReturn(List.of(subInfo1, subInfo2))
+ .when(mSubscriptionManager)
+ .getActiveSubscriptionInfoList();
mHandlerThread = new HandlerThread("KeepaliveStatsTrackerTest");
mHandlerThread.start();
mTestHandler = new Handler(mHandlerThread.getLooper());
- setUptimeMillis(0);
- mKeepaliveStatsTracker = new KeepaliveStatsTracker(mTestHandler, mDependencies);
+ setElapsedRealtime(0);
+ mKeepaliveStatsTracker = new KeepaliveStatsTracker(mContext, mTestHandler, mDependencies);
+ HandlerUtils.waitForIdle(BackgroundThread.getHandler(), TIMEOUT_MS);
+
+ // Initial onSubscriptionsChanged.
+ getOnSubscriptionsChangedListener().onSubscriptionsChanged();
+ HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
}
- private void setUptimeMillis(long time) {
- doReturn(time).when(mDependencies).getUptimeMillis();
+ private void setElapsedRealtime(long time) {
+ doReturn(time).when(mDependencies).getElapsedRealtime();
}
private DailykeepaliveInfoReported buildKeepaliveMetrics(long time) {
- setUptimeMillis(time);
+ setElapsedRealtime(time);
return visibleOnHandlerThread(
mTestHandler, () -> mKeepaliveStatsTracker.buildKeepaliveMetrics());
}
private DailykeepaliveInfoReported buildAndResetMetrics(long time) {
- setUptimeMillis(time);
+ setElapsedRealtime(time);
return visibleOnHandlerThread(
- mTestHandler,
- () -> {
- final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
- mKeepaliveStatsTracker.buildKeepaliveMetrics();
- mKeepaliveStatsTracker.resetMetrics();
- return dailyKeepaliveInfoReported;
- });
+ mTestHandler, () -> mKeepaliveStatsTracker.buildAndResetMetrics());
}
- private void onStartKeepalive(long time) {
- setUptimeMillis(time);
- visibleOnHandlerThread(mTestHandler, () -> mKeepaliveStatsTracker.onStartKeepalive());
+ private void onStartKeepalive(long time, int slot) {
+ onStartKeepalive(time, slot, TEST_KEEPALIVE_INTERVAL_SEC);
}
- private void onPauseKeepalive(long time) {
- setUptimeMillis(time);
- visibleOnHandlerThread(mTestHandler, () -> mKeepaliveStatsTracker.onPauseKeepalive());
+ private void onStartKeepalive(long time, int slot, int intervalSeconds) {
+ onStartKeepalive(time, slot, TEST_NETWORK_CAPABILITIES, intervalSeconds);
}
- private void onResumeKeepalive(long time) {
- setUptimeMillis(time);
- visibleOnHandlerThread(mTestHandler, () -> mKeepaliveStatsTracker.onResumeKeepalive());
+ private void onStartKeepalive(long time, int slot, NetworkCapabilities nc) {
+ onStartKeepalive(time, slot, nc, TEST_KEEPALIVE_INTERVAL_SEC);
}
- private void onStopKeepalive(long time, boolean wasActive) {
- setUptimeMillis(time);
+ private void onStartKeepalive(
+ long time, int slot, NetworkCapabilities nc, int intervalSeconds) {
+ onStartKeepalive(time, slot, nc, intervalSeconds, TEST_UID, /* isAutoKeepalive= */ true);
+ }
+
+ private void onStartKeepalive(long time, int slot, NetworkCapabilities nc, int intervalSeconds,
+ int uid, boolean isAutoKeepalive) {
+ setElapsedRealtime(time);
+ visibleOnHandlerThread(mTestHandler, () ->
+ mKeepaliveStatsTracker.onStartKeepalive(TEST_NETWORK, slot, nc, intervalSeconds,
+ uid, isAutoKeepalive));
+ }
+
+ private void onPauseKeepalive(long time, int slot) {
+ setElapsedRealtime(time);
visibleOnHandlerThread(
- mTestHandler, () -> mKeepaliveStatsTracker.onStopKeepalive(wasActive));
+ mTestHandler, () -> mKeepaliveStatsTracker.onPauseKeepalive(TEST_NETWORK, slot));
+ }
+
+ private void onResumeKeepalive(long time, int slot) {
+ setElapsedRealtime(time);
+ visibleOnHandlerThread(
+ mTestHandler, () -> mKeepaliveStatsTracker.onResumeKeepalive(TEST_NETWORK, slot));
+ }
+
+ private void onStopKeepalive(long time, int slot) {
+ setElapsedRealtime(time);
+ visibleOnHandlerThread(
+ mTestHandler, () -> mKeepaliveStatsTracker.onStopKeepalive(TEST_NETWORK, slot));
}
@Test
public void testEnsureRunningOnHandlerThread() {
// Not running on handler thread
- assertThrows(IllegalStateException.class, () -> mKeepaliveStatsTracker.onStartKeepalive());
- assertThrows(IllegalStateException.class, () -> mKeepaliveStatsTracker.onPauseKeepalive());
- assertThrows(IllegalStateException.class, () -> mKeepaliveStatsTracker.onResumeKeepalive());
assertThrows(
- IllegalStateException.class, () -> mKeepaliveStatsTracker.onStopKeepalive(true));
+ IllegalStateException.class,
+ () -> mKeepaliveStatsTracker.onStartKeepalive(
+ TEST_NETWORK,
+ TEST_SLOT,
+ TEST_NETWORK_CAPABILITIES,
+ TEST_KEEPALIVE_INTERVAL_SEC,
+ TEST_UID,
+ /* isAutoKeepalive */ true));
+ assertThrows(
+ IllegalStateException.class,
+ () -> mKeepaliveStatsTracker.onPauseKeepalive(TEST_NETWORK, TEST_SLOT));
+ assertThrows(
+ IllegalStateException.class,
+ () -> mKeepaliveStatsTracker.onResumeKeepalive(TEST_NETWORK, TEST_SLOT));
+ assertThrows(
+ IllegalStateException.class,
+ () -> mKeepaliveStatsTracker.onStopKeepalive(TEST_NETWORK, TEST_SLOT));
assertThrows(
IllegalStateException.class, () -> mKeepaliveStatsTracker.buildKeepaliveMetrics());
assertThrows(
- IllegalStateException.class, () -> mKeepaliveStatsTracker.resetMetrics());
+ IllegalStateException.class, () -> mKeepaliveStatsTracker.buildAndResetMetrics());
}
/**
@@ -133,45 +319,112 @@
* @param expectActiveDurations integer array where the index is the number of concurrent
* keepalives and the value is the expected duration of time that the tracker is in a state
* with the given number of keepalives active.
- * @param resultDurationsPerNumOfKeepalive the DurationPerNumOfKeepalive message to assert.
+ * @param actualDurationsPerNumOfKeepalive the DurationPerNumOfKeepalive message to assert.
*/
private void assertDurationMetrics(
int[] expectRegisteredDurations,
int[] expectActiveDurations,
- DurationPerNumOfKeepalive resultDurationsPerNumOfKeepalive) {
+ DurationPerNumOfKeepalive actualDurationsPerNumOfKeepalive) {
final int maxNumOfKeepalive = expectRegisteredDurations.length;
assertEquals(maxNumOfKeepalive, expectActiveDurations.length);
assertEquals(
maxNumOfKeepalive,
- resultDurationsPerNumOfKeepalive.getDurationForNumOfKeepaliveCount());
+ actualDurationsPerNumOfKeepalive.getDurationForNumOfKeepaliveCount());
for (int numOfKeepalive = 0; numOfKeepalive < maxNumOfKeepalive; numOfKeepalive++) {
- final DurationForNumOfKeepalive resultDurations =
- resultDurationsPerNumOfKeepalive.getDurationForNumOfKeepalive(numOfKeepalive);
+ final DurationForNumOfKeepalive actualDurations =
+ actualDurationsPerNumOfKeepalive.getDurationForNumOfKeepalive(numOfKeepalive);
- assertEquals(numOfKeepalive, resultDurations.getNumOfKeepalive());
+ assertEquals(numOfKeepalive, actualDurations.getNumOfKeepalive());
assertEquals(
expectRegisteredDurations[numOfKeepalive],
- resultDurations.getKeepaliveRegisteredDurationsMsec());
+ actualDurations.getKeepaliveRegisteredDurationsMsec());
assertEquals(
expectActiveDurations[numOfKeepalive],
- resultDurations.getKeepaliveActiveDurationsMsec());
+ actualDurations.getKeepaliveActiveDurationsMsec());
+ }
+ }
+
+ /**
+ * Asserts the actual KeepaliveLifetimePerCarrier contains an expected KeepaliveCarrierStats.
+ * This finds and checks only for the (carrierId, transportTypes, intervalMs) of the given
+ * expectKeepaliveCarrierStats and asserts the lifetime metrics.
+ *
+ * @param expectKeepaliveCarrierStats a keepalive lifetime metric that is expected to be in the
+ * proto.
+ * @param actualKeepaliveLifetimePerCarrier the KeepaliveLifetimePerCarrier message to assert.
+ */
+ private void findAndAssertCarrierLifetimeMetrics(
+ KeepaliveCarrierStats expectKeepaliveCarrierStats,
+ KeepaliveLifetimePerCarrier actualKeepaliveLifetimePerCarrier) {
+ for (KeepaliveLifetimeForCarrier keepaliveLifetimeForCarrier :
+ actualKeepaliveLifetimePerCarrier.getKeepaliveLifetimeForCarrierList()) {
+ if (expectKeepaliveCarrierStats.carrierId == keepaliveLifetimeForCarrier.getCarrierId()
+ && expectKeepaliveCarrierStats.transportTypes
+ == keepaliveLifetimeForCarrier.getTransportTypes()
+ && expectKeepaliveCarrierStats.intervalMs
+ == keepaliveLifetimeForCarrier.getIntervalsMsec()) {
+ assertEquals(
+ expectKeepaliveCarrierStats.lifetimeMs,
+ keepaliveLifetimeForCarrier.getLifetimeMsec());
+ assertEquals(
+ expectKeepaliveCarrierStats.activeLifetimeMs,
+ keepaliveLifetimeForCarrier.getActiveLifetimeMsec());
+ return;
+ }
+ }
+ fail("KeepaliveLifetimeForCarrier not found for a given expected KeepaliveCarrierStats");
+ }
+
+ private void assertNoDuplicates(Object[] arr) {
+ final Set<Object> s = new HashSet<Object>(Arrays.asList(arr));
+ assertEquals(arr.length, s.size());
+ }
+
+ /**
+ * Asserts that a KeepaliveLifetimePerCarrier contains all the expected KeepaliveCarrierStats.
+ *
+ * @param expectKeepaliveCarrierStatsArray an array of keepalive lifetime metrics that is
+ * expected to be in the KeepaliveLifetimePerCarrier.
+ * @param actualKeepaliveLifetimePerCarrier the KeepaliveLifetimePerCarrier message to assert.
+ */
+ private void assertCarrierLifetimeMetrics(
+ KeepaliveCarrierStats[] expectKeepaliveCarrierStatsArray,
+ KeepaliveLifetimePerCarrier actualKeepaliveLifetimePerCarrier) {
+ assertNoDuplicates(expectKeepaliveCarrierStatsArray);
+ assertEquals(
+ expectKeepaliveCarrierStatsArray.length,
+ actualKeepaliveLifetimePerCarrier.getKeepaliveLifetimeForCarrierCount());
+ for (KeepaliveCarrierStats keepaliveCarrierStats : expectKeepaliveCarrierStatsArray) {
+ findAndAssertCarrierLifetimeMetrics(
+ keepaliveCarrierStats, actualKeepaliveLifetimePerCarrier);
}
}
private void assertDailyKeepaliveInfoReported(
DailykeepaliveInfoReported dailyKeepaliveInfoReported,
+ int expectRequestsCount,
+ int expectAutoRequestsCount,
+ int[] expectAppUids,
int[] expectRegisteredDurations,
- int[] expectActiveDurations) {
- // TODO(b/273451360) Assert these values when they are filled.
- assertFalse(dailyKeepaliveInfoReported.hasKeepaliveLifetimePerCarrier());
- assertFalse(dailyKeepaliveInfoReported.hasKeepaliveRequests());
- assertFalse(dailyKeepaliveInfoReported.hasAutomaticKeepaliveRequests());
- assertFalse(dailyKeepaliveInfoReported.hasDistinctUserCount());
- assertTrue(dailyKeepaliveInfoReported.getUidList().isEmpty());
+ int[] expectActiveDurations,
+ KeepaliveCarrierStats[] expectKeepaliveCarrierStatsArray) {
+ assertEquals(expectRequestsCount, dailyKeepaliveInfoReported.getKeepaliveRequests());
+ assertEquals(
+ expectAutoRequestsCount,
+ dailyKeepaliveInfoReported.getAutomaticKeepaliveRequests());
+ assertEquals(expectAppUids.length, dailyKeepaliveInfoReported.getDistinctUserCount());
- final DurationPerNumOfKeepalive resultDurations =
+ final int[] uidArray = CollectionUtils.toIntArray(dailyKeepaliveInfoReported.getUidList());
+ assertArrayEquals(expectAppUids, uidArray);
+
+ final DurationPerNumOfKeepalive actualDurations =
dailyKeepaliveInfoReported.getDurationPerNumOfKeepalive();
- assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, actualDurations);
+
+ final KeepaliveLifetimePerCarrier actualCarrierLifetime =
+ dailyKeepaliveInfoReported.getKeepaliveLifetimePerCarrier();
+
+ assertCarrierLifetimeMetrics(expectKeepaliveCarrierStatsArray, actualCarrierLifetime);
}
@Test
@@ -187,8 +440,12 @@
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 0,
+ /* expectAutoRequestsCount= */ 0,
+ /* expectAppUids= */ new int[0],
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[0]);
}
/*
@@ -203,7 +460,7 @@
final int startTime = 1000;
final int writeTime = 5000;
- onStartKeepalive(startTime);
+ onStartKeepalive(startTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildKeepaliveMetrics(writeTime);
@@ -214,8 +471,14 @@
final int[] expectActiveDurations = new int[] {startTime, writeTime - startTime};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
}
/*
@@ -231,9 +494,9 @@
final int pauseTime = 2030;
final int writeTime = 5000;
- onStartKeepalive(startTime);
+ onStartKeepalive(startTime, TEST_SLOT);
- onPauseKeepalive(pauseTime);
+ onPauseKeepalive(pauseTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildKeepaliveMetrics(writeTime);
@@ -246,8 +509,14 @@
new int[] {startTime + (writeTime - pauseTime), pauseTime - startTime};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
}
/*
@@ -264,11 +533,11 @@
final int resumeTime = 3450;
final int writeTime = 5000;
- onStartKeepalive(startTime);
+ onStartKeepalive(startTime, TEST_SLOT);
- onPauseKeepalive(pauseTime);
+ onPauseKeepalive(pauseTime, TEST_SLOT);
- onResumeKeepalive(resumeTime);
+ onResumeKeepalive(resumeTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildKeepaliveMetrics(writeTime);
@@ -284,8 +553,14 @@
};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
}
/*
@@ -303,13 +578,13 @@
final int stopTime = 4157;
final int writeTime = 5000;
- onStartKeepalive(startTime);
+ onStartKeepalive(startTime, TEST_SLOT);
- onPauseKeepalive(pauseTime);
+ onPauseKeepalive(pauseTime, TEST_SLOT);
- onResumeKeepalive(resumeTime);
+ onResumeKeepalive(resumeTime, TEST_SLOT);
- onStopKeepalive(stopTime, /* wasActive= */ true);
+ onStopKeepalive(stopTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildKeepaliveMetrics(writeTime);
@@ -326,8 +601,14 @@
};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
}
/*
@@ -344,11 +625,11 @@
final int stopTime = 4157;
final int writeTime = 5000;
- onStartKeepalive(startTime);
+ onStartKeepalive(startTime, TEST_SLOT);
- onPauseKeepalive(pauseTime);
+ onPauseKeepalive(pauseTime, TEST_SLOT);
- onStopKeepalive(stopTime, /* wasActive= */ false);
+ onStopKeepalive(stopTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildKeepaliveMetrics(writeTime);
@@ -362,8 +643,14 @@
new int[] {startTime + (writeTime - pauseTime), (pauseTime - startTime)};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
}
/*
@@ -381,17 +668,17 @@
final int stopTime = 4000;
final int writeTime = 5000;
- onStartKeepalive(startTime);
+ onStartKeepalive(startTime, TEST_SLOT);
for (int i = 0; i < pauseResumeTimes.length; i++) {
if (i % 2 == 0) {
- onPauseKeepalive(pauseResumeTimes[i]);
+ onPauseKeepalive(pauseResumeTimes[i], TEST_SLOT);
} else {
- onResumeKeepalive(pauseResumeTimes[i]);
+ onResumeKeepalive(pauseResumeTimes[i], TEST_SLOT);
}
}
- onStopKeepalive(stopTime, /* wasActive= */ true);
+ onStopKeepalive(stopTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildKeepaliveMetrics(writeTime);
@@ -407,8 +694,14 @@
};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
}
/*
@@ -431,19 +724,19 @@
final int stopTime1 = 4157;
final int writeTime = 5000;
- onStartKeepalive(startTime1);
+ onStartKeepalive(startTime1, TEST_SLOT);
- onPauseKeepalive(pauseTime1);
+ onPauseKeepalive(pauseTime1, TEST_SLOT);
- onStartKeepalive(startTime2);
+ onStartKeepalive(startTime2, TEST_SLOT2);
- onResumeKeepalive(resumeTime1);
+ onResumeKeepalive(resumeTime1, TEST_SLOT);
- onPauseKeepalive(pauseTime2);
+ onPauseKeepalive(pauseTime2, TEST_SLOT2);
- onResumeKeepalive(resumeTime2);
+ onResumeKeepalive(resumeTime2, TEST_SLOT2);
- onStopKeepalive(stopTime1, /* wasActive= */ true);
+ onStopKeepalive(stopTime1, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildKeepaliveMetrics(writeTime);
@@ -474,10 +767,21 @@
// 2 active keepalives before keepalive2 is paused and before keepalive1 stops.
(pauseTime2 - resumeTime1) + (stopTime1 - resumeTime2)
};
+
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 2,
+ /* expectAutoRequestsCount= */ 2,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ // The carrier stats are aggregated here since the keepalives have the same
+ // (carrierId, transportTypes, intervalMs).
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(
+ expectRegisteredDurations[1] + 2 * expectRegisteredDurations[2],
+ expectActiveDurations[1] + 2 * expectActiveDurations[2])
+ });
}
/*
@@ -494,7 +798,7 @@
final int stopTime = 7000;
final int writeTime2 = 10000;
- onStartKeepalive(startTime);
+ onStartKeepalive(startTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
buildAndResetMetrics(writeTime);
@@ -504,20 +808,31 @@
final int[] expectActiveDurations = new int[] {startTime, writeTime - startTime};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations,
- expectActiveDurations);
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
+ // Check metrics was reset from above.
final DailykeepaliveInfoReported dailyKeepaliveInfoReported2 =
buildKeepaliveMetrics(writeTime);
// Expect the stored durations to be 0 but still contain the number of keepalive = 1.
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported2,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
/* expectRegisteredDurations= */ new int[] {0, 0},
- /* expectActiveDurations= */ new int[] {0, 0});
+ /* expectActiveDurations= */ new int[] {0, 0},
+ new KeepaliveCarrierStats[] {getDefaultCarrierStats(0, 0)});
// Expect that the keepalive is still registered after resetting so it can be stopped.
- onStopKeepalive(stopTime, /* wasActive= */ true);
+ onStopKeepalive(stopTime, TEST_SLOT);
final DailykeepaliveInfoReported dailyKeepaliveInfoReported3 =
buildKeepaliveMetrics(writeTime2);
@@ -528,7 +843,353 @@
new int[] {writeTime2 - stopTime, stopTime - writeTime};
assertDailyKeepaliveInfoReported(
dailyKeepaliveInfoReported3,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
expectRegisteredDurations2,
- expectActiveDurations2);
+ expectActiveDurations2,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations2[1], expectActiveDurations2[1])
+ });
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive1 S1 S1 W+reset W
+ * Keepalive2 S2 W+reset W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testResetMetrics_twoKeepalives() {
+ final int startTime1 = 1000;
+ final int startTime2 = 2000;
+ final int stopTime1 = 4157;
+ final int writeTime = 5000;
+ final int writeTime2 = 10000;
+
+ onStartKeepalive(startTime1, TEST_SLOT);
+
+ onStartKeepalive(startTime2, TEST_SLOT2, TEST_NETWORK_CAPABILITIES_2,
+ TEST_KEEPALIVE_INTERVAL2_SEC);
+
+ onStopKeepalive(stopTime1, TEST_SLOT);
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ buildAndResetMetrics(writeTime);
+
+ final int[] expectRegisteredDurations =
+ new int[] {
+ startTime1,
+ // 1 keepalive before keepalive2 starts and after keepalive1 stops.
+ (startTime2 - startTime1) + (writeTime - stopTime1),
+ stopTime1 - startTime2
+ };
+ // Since there is no pause, expect the same as registered durations.
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime1,
+ (startTime2 - startTime1) + (writeTime - stopTime1),
+ stopTime1 - startTime2
+ };
+
+ // Lifetime carrier stats are independent of each other since they have different intervals.
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats1 =
+ getDefaultCarrierStats(stopTime1 - startTime1, stopTime1 - startTime1);
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats2 =
+ new KeepaliveCarrierStats(
+ TEST_CARRIER_ID_2,
+ /* transportTypes= */ (1 << TRANSPORT_CELLULAR),
+ TEST_KEEPALIVE_INTERVAL2_SEC * 1000,
+ writeTime - startTime2,
+ writeTime - startTime2);
+
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 2,
+ /* expectAutoRequestsCount= */ 2,
+ /* expectAppUids= */ new int[] {TEST_UID},
+ expectRegisteredDurations,
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ expectKeepaliveCarrierStats1, expectKeepaliveCarrierStats2
+ });
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported2 =
+ buildKeepaliveMetrics(writeTime2);
+
+ // Only 1 keepalive is registered and active since the reset until the writeTime2.
+ final int[] expectRegisteredDurations2 = new int[] {0, writeTime2 - writeTime};
+ final int[] expectActiveDurations2 = new int[] {0, writeTime2 - writeTime};
+
+ // Only the keepalive with interval of intervalSec2 is present.
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats3 =
+ new KeepaliveCarrierStats(
+ TEST_CARRIER_ID_2,
+ /* transportTypes= */ (1 << TRANSPORT_CELLULAR),
+ TEST_KEEPALIVE_INTERVAL2_SEC * 1000,
+ writeTime2 - writeTime,
+ writeTime2 - writeTime);
+
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported2,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
+ expectRegisteredDurations2,
+ expectActiveDurations2,
+ new KeepaliveCarrierStats[] {expectKeepaliveCarrierStats3});
+ }
+
+ @Test
+ public void testReusableSlot_keepaliveNotStopped() {
+ final int startTime1 = 1000;
+ final int startTime2 = 2000;
+ final int writeTime = 5000;
+
+ onStartKeepalive(startTime1, TEST_SLOT);
+
+ // Attempt to use the same (network, slot)
+ assertThrows(IllegalArgumentException.class, () -> onStartKeepalive(startTime2, TEST_SLOT));
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ buildKeepaliveMetrics(writeTime);
+
+ // Expect the duration to be from startTime1 and not startTime2, it should not start again.
+ final int[] expectRegisteredDurations = new int[] {startTime1, writeTime - startTime1};
+ final int[] expectActiveDurations = new int[] {startTime1, writeTime - startTime1};
+
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
+ expectRegisteredDurations,
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
+ }
+
+ @Test
+ public void testReusableSlot_keepaliveStopped() {
+ final int startTime1 = 1000;
+ final int stopTime = 2000;
+ final int startTime2 = 3000;
+ final int writeTime = 5000;
+
+ onStartKeepalive(startTime1, TEST_SLOT);
+
+ onStopKeepalive(stopTime, TEST_SLOT);
+
+ // Attempt to use the same (network, slot)
+ onStartKeepalive(startTime2, TEST_SLOT);
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ buildKeepaliveMetrics(writeTime);
+
+ // Expect the durations to be an aggregate of both periods.
+ // i.e. onStartKeepalive works on the same (network, slot) if it has been stopped.
+ final int[] expectRegisteredDurations =
+ new int[] {
+ startTime1 + (startTime2 - stopTime),
+ (stopTime - startTime1) + (writeTime - startTime2)
+ };
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime1 + (startTime2 - stopTime),
+ (stopTime - startTime1) + (writeTime - startTime2)
+ };
+
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 2,
+ /* expectAutoRequestsCount= */ 2,
+ /* expectAppUids= */ new int[] {TEST_UID},
+ expectRegisteredDurations,
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(expectRegisteredDurations[1], expectActiveDurations[1])
+ });
+ }
+
+ @Test
+ public void testCarrierIdChange_changeBeforeStart() {
+ // Update the list to only have sub_id_2 with carrier_id_1.
+ final SubscriptionInfo subInfo = makeSubInfoMock(TEST_SUB_ID_2, TEST_CARRIER_ID_1);
+ doReturn(List.of(subInfo)).when(mSubscriptionManager).getActiveSubscriptionInfoList();
+
+ getOnSubscriptionsChangedListener().onSubscriptionsChanged();
+ HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
+
+ final int startTime = 1000;
+ final int writeTime = 5000;
+
+ onStartKeepalive(startTime, TEST_SLOT, TEST_NETWORK_CAPABILITIES);
+ onStartKeepalive(startTime, TEST_SLOT2, TEST_NETWORK_CAPABILITIES_2);
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ buildKeepaliveMetrics(writeTime);
+
+ // The network with sub_id_1 has an unknown carrier id.
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats1 =
+ new KeepaliveCarrierStats(
+ TelephonyManager.UNKNOWN_CARRIER_ID,
+ /* transportTypes= */ (1 << TRANSPORT_CELLULAR),
+ TEST_KEEPALIVE_INTERVAL_SEC * 1000,
+ writeTime - startTime,
+ writeTime - startTime);
+
+ // The network with sub_id_2 has carrier_id_1.
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats2 =
+ new KeepaliveCarrierStats(
+ TEST_CARRIER_ID_1,
+ /* transportTypes= */ (1 << TRANSPORT_CELLULAR),
+ TEST_KEEPALIVE_INTERVAL_SEC * 1000,
+ writeTime - startTime,
+ writeTime - startTime);
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 2,
+ /* expectAutoRequestsCount= */ 2,
+ /* expectAppUids= */ new int[] {TEST_UID},
+ /* expectRegisteredDurations= */ new int[] {startTime, 0, writeTime - startTime},
+ /* expectActiveDurations= */ new int[] {startTime, 0, writeTime - startTime},
+ new KeepaliveCarrierStats[] {
+ expectKeepaliveCarrierStats1, expectKeepaliveCarrierStats2
+ });
+ }
+
+ @Test
+ public void testCarrierIdFromWifiInfo() {
+ final int startTime = 1000;
+ final int writeTime = 5000;
+
+ final WifiInfo wifiInfo = mock(WifiInfo.class);
+ final WifiInfo wifiInfoCopy = mock(WifiInfo.class);
+
+ // Building NetworkCapabilities stores a copy of the WifiInfo with makeCopy.
+ doReturn(wifiInfoCopy).when(wifiInfo).makeCopy(anyLong());
+ doReturn(TEST_SUB_ID_1).when(wifiInfo).getSubscriptionId();
+ doReturn(TEST_SUB_ID_1).when(wifiInfoCopy).getSubscriptionId();
+ final NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(wifiInfo)
+ .build();
+
+ onStartKeepalive(startTime, TEST_SLOT, nc);
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ buildKeepaliveMetrics(writeTime);
+
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats =
+ new KeepaliveCarrierStats(
+ TEST_CARRIER_ID_1,
+ /* transportTypes= */ (1 << TRANSPORT_WIFI),
+ TEST_KEEPALIVE_INTERVAL_SEC * 1000,
+ writeTime - startTime,
+ writeTime - startTime);
+
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 1,
+ /* expectAutoRequestsCount= */ 1,
+ /* expectAppUids= */ new int[] {TEST_UID},
+ /* expectRegisteredDurations= */ new int[] {startTime, writeTime - startTime},
+ /* expectActiveDurations= */ new int[] {startTime, writeTime - startTime},
+ new KeepaliveCarrierStats[] {expectKeepaliveCarrierStats});
+ }
+
+ @Test
+ public void testKeepaliveCountsAndUids() {
+ final int startTime1 = 1000, startTime2 = 2000, startTime3 = 3000;
+ final int writeTime = 5000;
+ final int[] uids = new int[] {TEST_UID, TEST_UID + 1, TEST_UID + 2};
+ onStartKeepalive(startTime1, TEST_SLOT, TEST_NETWORK_CAPABILITIES,
+ TEST_KEEPALIVE_INTERVAL_SEC, uids[0], /* isAutoKeepalive= */ true);
+ onStartKeepalive(startTime2, TEST_SLOT + 1, TEST_NETWORK_CAPABILITIES,
+ TEST_KEEPALIVE_INTERVAL_SEC, uids[1], /* isAutoKeepalive= */ false);
+ onStartKeepalive(startTime3, TEST_SLOT + 2, TEST_NETWORK_CAPABILITIES,
+ TEST_KEEPALIVE_INTERVAL_SEC, uids[2], /* isAutoKeepalive= */ true);
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ buildKeepaliveMetrics(writeTime);
+ final int[] expectRegisteredDurations =
+ new int[] {
+ startTime1,
+ (startTime2 - startTime1),
+ (startTime3 - startTime2),
+ (writeTime - startTime3)
+ };
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime1,
+ (startTime2 - startTime1),
+ (startTime3 - startTime2),
+ (writeTime - startTime3)
+ };
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 3,
+ /* expectAutoRequestsCount= */ 2,
+ /* expectAppUids= */ uids,
+ expectRegisteredDurations,
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ getDefaultCarrierStats(
+ writeTime * 3 - startTime1 - startTime2 - startTime3,
+ writeTime * 3 - startTime1 - startTime2 - startTime3)
+ });
+ }
+
+ @Test
+ public void testUpdateDefaultSubId() {
+ final int startTime1 = 1000;
+ final int startTime2 = 3000;
+ final int writeTime = 5000;
+
+ // No TelephonyNetworkSpecifier set with subId to force the use of default subId.
+ final NetworkCapabilities nc =
+ new NetworkCapabilities.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+ onStartKeepalive(startTime1, TEST_SLOT, nc);
+ // Update default subId
+ triggerBroadcastDefaultSubId(TEST_SUB_ID_1);
+ onStartKeepalive(startTime2, TEST_SLOT2, nc);
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ buildKeepaliveMetrics(writeTime);
+
+ final int[] expectRegisteredDurations =
+ new int[] {startTime1, startTime2 - startTime1, writeTime - startTime2};
+ final int[] expectActiveDurations =
+ new int[] {startTime1, startTime2 - startTime1, writeTime - startTime2};
+ // Expect the carrier id of the first keepalive to be unknown
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats1 =
+ new KeepaliveCarrierStats(
+ TelephonyManager.UNKNOWN_CARRIER_ID,
+ /* transportTypes= */ (1 << TRANSPORT_CELLULAR),
+ TEST_KEEPALIVE_INTERVAL_SEC * 1000,
+ writeTime - startTime1,
+ writeTime - startTime1);
+ // Expect the carrier id of the second keepalive to be TEST_CARRIER_ID_1, from TEST_SUB_ID_1
+ final KeepaliveCarrierStats expectKeepaliveCarrierStats2 =
+ new KeepaliveCarrierStats(
+ TEST_CARRIER_ID_1,
+ /* transportTypes= */ (1 << TRANSPORT_CELLULAR),
+ TEST_KEEPALIVE_INTERVAL_SEC * 1000,
+ writeTime - startTime2,
+ writeTime - startTime2);
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ /* expectRequestsCount= */ 2,
+ /* expectAutoRequestsCount= */ 2,
+ /* expectAppUids= */ new int[] {TEST_UID},
+ expectRegisteredDurations,
+ expectActiveDurations,
+ new KeepaliveCarrierStats[] {
+ expectKeepaliveCarrierStats1, expectKeepaliveCarrierStats2
+ });
}
}