Invoke onConnectivityReport on registering ConnectivityDiagnostics.
This change updates the behavior for registering
ConnectivityDiagnosticsCallbacks. Now, after a successful register()
call, callbacks will receive cached ConnectivityReports for all
matching, permissioned networks. This allows registrants to be updated
with the network state for their networks without having to wait for the
next network validation.
Bug: 147849853
Test: atest FrameworksNetTests
Change-Id: I924ba8fdcc847f453557021591bde38602fe089c
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 6f0a4f9..aa35852 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -659,7 +659,8 @@
public abstract static class ConnectivityDiagnosticsCallback {
/**
* Called when the platform completes a data connectivity check. This will also be invoked
- * upon registration with the latest report.
+ * immediately upon registration with the latest report, if a report has already been
+ * generated for this network.
*
* <p>The Network specified in the ConnectivityReport may not be active any more when this
* method is invoked.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 42fa099..11a5f1b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -7816,6 +7816,34 @@
cb.asBinder().linkToDeath(cbInfo, 0);
} catch (RemoteException e) {
cbInfo.binderDied();
+ return;
+ }
+
+ // Once registered, provide ConnectivityReports for matching Networks
+ final List<NetworkAgentInfo> matchingNetworks = new ArrayList<>();
+ synchronized (mNetworkForNetId) {
+ for (int i = 0; i < mNetworkForNetId.size(); i++) {
+ final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
+ if (nai.satisfies(nri.request)) {
+ matchingNetworks.add(nai);
+ }
+ }
+ }
+ for (final NetworkAgentInfo nai : matchingNetworks) {
+ final ConnectivityReport report = nai.getConnectivityReport();
+ if (report == null) {
+ continue;
+ }
+ if (!checkConnectivityDiagnosticsPermissions(
+ nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+ continue;
+ }
+
+ try {
+ cb.onConnectivityReportAvailable(report);
+ } catch (RemoteException e) {
+ // Exception while sending the ConnectivityReport. Move on to the next network.
+ }
}
}
@@ -7851,6 +7879,7 @@
nai.linkProperties,
networkCapabilities,
extras);
+ nai.setConnectivityReport(report);
final List<IConnectivityDiagnosticsCallback> results =
getMatchingPermissionedCallbacks(nai);
for (final IConnectivityDiagnosticsCallback cb : results) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index f844844..2ad44b2 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import static android.net.NetworkCapabilities.transportNamesOf;
import android.annotation.NonNull;
@@ -243,6 +244,9 @@
// How many of the satisfied requests are of type BACKGROUND_REQUEST.
private int mNumBackgroundNetworkRequests = 0;
+ // The last ConnectivityReport made available for this network.
+ private ConnectivityReport mConnectivityReport;
+
public final Messenger messenger;
public final AsyncChannel asyncChannel;
@@ -621,6 +625,32 @@
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
+ /**
+ * Sets the most recent ConnectivityReport for this network.
+ *
+ * <p>This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ public void setConnectivityReport(@NonNull ConnectivityReport connectivityReport) {
+ mConnectivityReport = connectivityReport;
+ }
+
+ /**
+ * Returns the most recent ConnectivityReport for this network, or null if none have been
+ * reported yet.
+ *
+ * <p>This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ @Nullable
+ public ConnectivityReport getConnectivityReport() {
+ return mConnectivityReport;
+ }
+
+ // TODO: Print shorter members first and only print the boolean variable which value is true
+ // to improve readability.
public String toString() {
return "NetworkAgentInfo{"
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 83399b8..1d7d3c0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -316,6 +316,8 @@
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final String INTERFACE_NAME = "interface";
+
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private ConnectivityService mService;
@@ -6909,6 +6911,38 @@
mContext.getOpPackageName()));
}
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
+ throws Exception {
+ // Set up the Network, which leads to a ConnectivityReport being cached for the network.
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, linkProperties);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callback.assertNoCallback();
+
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ verify(mConnectivityDiagnosticsCallback)
+ .onConnectivityReportAvailable(argThat(report -> {
+ return INTERFACE_NAME.equals(report.getLinkProperties().getInterfaceName())
+ && report.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR);
+ }));
+ }
+
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);