Merge changes I3a38af76,I5e21de05
* changes:
Register BroadcastReceiver for default subId.
Write keepalive metrics periodically using alarms.
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 981bd4d..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.
*
@@ -311,7 +315,26 @@
mContext, mConnectivityServiceHandler);
mAlarmManager = mDependencies.getAlarmManager(context);
- mKeepaliveStatsTracker = new KeepaliveStatsTracker(context, 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) {
@@ -898,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 ff9bb70..d59d526 100644
--- a/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
@@ -19,7 +19,10 @@
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;
@@ -42,6 +45,8 @@
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;
@@ -67,9 +72,7 @@
// 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.
+ // 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.
@@ -267,6 +270,19 @@
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,
@@ -284,11 +300,8 @@
// 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(),
@@ -592,6 +605,7 @@
*
* @return the DailykeepaliveInfoReported proto that was built.
*/
+ @VisibleForTesting
public @NonNull DailykeepaliveInfoReported buildAndResetMetrics() {
ensureRunningOnHandlerThread();
final long timeNow = mDependencies.getElapsedRealtime();
@@ -620,6 +634,20 @@
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() {
if (mConnectivityServiceHandler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException(
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index 0aecd64..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;
@@ -42,6 +43,7 @@
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;
@@ -125,6 +127,7 @@
@Mock NetworkAgentInfo mNai;
@Mock SubscriptionManager mSubscriptionManager;
+ KeepaliveStatsTracker mKeepaliveStatsTracker;
TestKeepaliveTracker mKeepaliveTracker;
AOOTestHandler mTestHandler;
TestTcpKeepaliveController mTcpController;
@@ -344,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);
}
@@ -499,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);
diff --git a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
index 9472ded..0d2e540 100644
--- a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
@@ -27,12 +27,15 @@
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;
@@ -109,6 +112,18 @@
@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);
@@ -1128,4 +1143,53 @@
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
+ });
+ }
}