From c48d856976c9fcb8150f4de524d69cc5d68ce46d Mon Sep 17 00:00:00 2001 From: Hansen Kurli Date: Wed, 19 Apr 2023 13:18:32 +0000 Subject: [PATCH] Register OnSubscriptionsChangedListener and cache results. Register a listener to get notified of SubscriptionInfo changes and store all carrierIds of active subscriptions in a cache. The executor for the listener callback runs on a different thread to the connectivity thread but posts the SubscriptionInfo list to the connectivity thread for caching. Bug: 273451360 Test: atest FrameworksNetTests Change-Id: I889d4da725ccda713367309c257622a0bf9939f3 --- .../AutomaticOnOffKeepaliveTracker.java | 2 +- .../connectivity/KeepaliveStatsTracker.java | 96 ++++++++- .../server/ConnectivityServiceTest.java | 3 + .../AutomaticOnOffKeepaliveTrackerTest.java | 15 ++ .../KeepaliveStatsTrackerTest.java | 185 ++++++++++++++++-- 5 files changed, 278 insertions(+), 23 deletions(-) diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java index e487217016..031d4c7232 100644 --- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java +++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java @@ -311,7 +311,7 @@ public class AutomaticOnOffKeepaliveTracker { mContext, mConnectivityServiceHandler); mAlarmManager = mDependencies.getAlarmManager(context); - mKeepaliveStatsTracker = new KeepaliveStatsTracker(handler); + mKeepaliveStatsTracker = new KeepaliveStatsTracker(context, handler); } private void startTcpPollingAlarm(@NonNull AutomaticOnOffKeepalive ki) { diff --git a/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java index 81345ab9dc..9f08673284 100644 --- a/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java +++ b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java @@ -16,14 +16,24 @@ package com.android.server.connectivity; +import static android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; + import android.annotation.NonNull; +import android.content.Context; 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; @@ -31,6 +41,7 @@ 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 java.util.ArrayList; import java.util.HashMap; @@ -52,6 +63,14 @@ public class KeepaliveStatsTracker { @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 done from the OnSubscriptionsChangedListener. Note that there is no callback done + // to OnSubscriptionsChangedListener when the default sub id changes. + // TODO: Register a listener for the default subId when it is possible. + 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. @@ -214,16 +233,53 @@ public class KeepaliveStatsTracker { } } - 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); + final SubscriptionManager subscriptionManager = + Objects.requireNonNull(context.getSystemService(SubscriptionManager.class)); + mLastUpdateDurationsTimestamp = mDependencies.getUptimeMillis(); + + // 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 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; + final int defaultSubId = + subscriptionManager.getDefaultSubscriptionId(); + mConnectivityServiceHandler.post(() -> { + mCachedCarrierIdPerSubId.clear(); + mCachedDefaultSubscriptionId = defaultSubId; + + 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. */ @@ -279,11 +335,33 @@ public class KeepaliveStatsTracker { mLastUpdateDurationsTimestamp = timeNow; } - // TODO(b/273451360): Make use of SubscriptionManager.OnSubscriptionsChangedListener since - // TelephonyManager.getSimCarrierId will be a cross-process call. - private int getCarrierId() { - // No implementation yet. - return TelephonyManager.UNKNOWN_CARRIER_ID; + // 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(); + } + } + + 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) { @@ -313,7 +391,7 @@ public class KeepaliveStatsTracker { final KeepaliveStats newKeepaliveStats = new KeepaliveStats( - getCarrierId(), getTransportTypes(nc), intervalSeconds, timeNow); + getCarrierId(nc), getTransportTypes(nc), intervalSeconds, timeNow); mKeepaliveStatsPerId.put(keepaliveId, newKeepaliveStats); } diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 43c6225bf1..57c3acc9a2 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.os.UserManager; 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 @@ public class ConnectivityServiceTest { @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 @@ public class ConnectivityServiceTest { 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); } diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java index e3d5c642dc..0aecd64ac7 100644 --- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java +++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java @@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.anyInt; 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; @@ -67,6 +68,7 @@ import android.os.IBinder; 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,6 +123,7 @@ public class AutomaticOnOffKeepaliveTrackerTest { @Mock Context mCtx; @Mock AlarmManager mAlarmManager; @Mock NetworkAgentInfo mNai; + @Mock SubscriptionManager mSubscriptionManager; TestKeepaliveTracker mKeepaliveTracker; AOOTestHandler mTestHandler; @@ -298,10 +301,22 @@ public class AutomaticOnOffKeepaliveTrackerTest { } } + private void mockService(String serviceName, Class 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"); diff --git a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java index 369894df81..b469ccd82e 100644 --- a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java +++ b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java @@ -17,6 +17,7 @@ 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; @@ -25,13 +26,24 @@ 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.Mockito.doCallRealMethod; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import android.content.Context; 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; @@ -41,39 +53,68 @@ 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.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; - // Carrier id not yet implemented, assume it returns unknown for now. - private static final int TEST_CARRIER_ID = TelephonyManager.UNKNOWN_CARRIER_ID; + 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 = - new NetworkCapabilities.Builder().addTransportType(TRANSPORT_CELLULAR).build(); + buildCellNetworkCapabilitiesWithSubId(TEST_SUB_ID_1); + private static final NetworkCapabilities TEST_NETWORK_CAPABILITIES_2 = + buildCellNetworkCapabilitiesWithSubId(TEST_SUB_ID_2); + + 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 OnSubscriptionsChangedListener getOnSubscriptionsChangedListener() { + final ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class); + verify(mSubscriptionManager) + .addOnSubscriptionsChangedListener(any(), listenerCaptor.capture()); + return listenerCaptor.getValue(); + } private static final class KeepaliveCarrierStats { public final int carrierId; @@ -116,23 +157,53 @@ public class KeepaliveStatsTrackerTest { // Use the default test carrier id, transportType and keepalive interval. private KeepaliveCarrierStats getDefaultCarrierStats(int lifetimeMs, int activeLifetimeMs) { return new KeepaliveCarrierStats( - TEST_CARRIER_ID, + TEST_CARRIER_ID_1, /* transportTypes= */ (1 << TRANSPORT_CELLULAR), TEST_KEEPALIVE_INTERVAL_SEC * 1000, lifetimeMs, activeLifetimeMs); } + private void mockService(String serviceName, Class 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); + 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) { @@ -158,13 +229,18 @@ public class KeepaliveStatsTrackerTest { } private void onStartKeepalive(long time, int slot, int intervalSeconds) { + onStartKeepalive(time, slot, TEST_NETWORK_CAPABILITIES, intervalSeconds); + } + + private void onStartKeepalive(long time, int slot, NetworkCapabilities nc) { + onStartKeepalive(time, slot, nc, TEST_KEEPALIVE_INTERVAL_SEC); + } + + private void onStartKeepalive( + long time, int slot, NetworkCapabilities nc, int intervalSeconds) { setUptimeMillis(time); visibleOnHandlerThread(mTestHandler, () -> - mKeepaliveStatsTracker.onStartKeepalive( - TEST_NETWORK, - slot, - TEST_NETWORK_CAPABILITIES, - intervalSeconds)); + mKeepaliveStatsTracker.onStartKeepalive(TEST_NETWORK, slot, nc, intervalSeconds)); } private void onPauseKeepalive(long time, int slot) { @@ -732,7 +808,8 @@ public class KeepaliveStatsTrackerTest { onStartKeepalive(startTime1, TEST_SLOT); - onStartKeepalive(startTime2, TEST_SLOT2, TEST_KEEPALIVE_INTERVAL2_SEC); + onStartKeepalive(startTime2, TEST_SLOT2, TEST_NETWORK_CAPABILITIES_2, + TEST_KEEPALIVE_INTERVAL2_SEC); onStopKeepalive(stopTime1, TEST_SLOT); @@ -759,7 +836,7 @@ public class KeepaliveStatsTrackerTest { getDefaultCarrierStats(stopTime1 - startTime1, stopTime1 - startTime1); final KeepaliveCarrierStats expectKeepaliveCarrierStats2 = new KeepaliveCarrierStats( - TEST_CARRIER_ID, + TEST_CARRIER_ID_2, /* transportTypes= */ (1 << TRANSPORT_CELLULAR), TEST_KEEPALIVE_INTERVAL2_SEC * 1000, writeTime - startTime2, @@ -783,7 +860,7 @@ public class KeepaliveStatsTrackerTest { // Only the keepalive with interval of intervalSec2 is present. final KeepaliveCarrierStats expectKeepaliveCarrierStats3 = new KeepaliveCarrierStats( - TEST_CARRIER_ID, + TEST_CARRIER_ID_2, /* transportTypes= */ (1 << TRANSPORT_CELLULAR), TEST_KEEPALIVE_INTERVAL2_SEC * 1000, writeTime2 - writeTime, @@ -861,4 +938,86 @@ public class KeepaliveStatsTrackerTest { 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, + /* 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, + /* expectRegisteredDurations= */ new int[] {startTime, writeTime - startTime}, + /* expectActiveDurations= */ new int[] {startTime, writeTime - startTime}, + new KeepaliveCarrierStats[] {expectKeepaliveCarrierStats}); + } }