Report result SKIPPED in ConnDiags if the network is not validated.
This CL updates ConnectivityDiagnostics to report
NETWORK_VALIDATION_RESULT_SKIPPED when the platform does not validate
the reported Network. This CL also updates the behavior for
ConnectivityManager#reportNetworkConnectivity, such that it will always
generate a ConnectivityReport on the reported network. If the reported
connectivity does not match the known connectivity of this network, the
network is revalidated and a report is generated. Otherwise,
revalidation is not performed and the cached ConnectivityReport is sent
instead.
This CL also updates ConnDiags behavior for calls to
ConnectivityManager#reportNetworkConnectivity. Specifically, ConnDiags
callbacks are only notified for these calls if:
a) the call causes the Network to be re-validated, or
b) the callback registrant was the caller of
#reportNetworkConnectivity().
For b), the caller is always guaranteed to receive a ConnectivityReport
(a fresh report if the Network is re-validated, else the cached report).
Bug: 162407730
Test: atest FrameworksNetTests ConnectivityDiagnosticsManagerTest
Change-Id: I78b78919d5b0f09348dfdd5fdb37418b8c7f861f
This commit is contained in:
@@ -55,6 +55,7 @@ import static android.net.ConnectivityManager.isNetworkTypeValid;
|
||||
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
|
||||
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
|
||||
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
|
||||
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_SKIPPED;
|
||||
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
|
||||
@@ -541,9 +542,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
private static final int EVENT_SET_AVOID_UNVALIDATED = 35;
|
||||
|
||||
/**
|
||||
* used to trigger revalidation of a network.
|
||||
* used to handle reported network connectivity. May trigger revalidation of a network.
|
||||
*/
|
||||
private static final int EVENT_REVALIDATE_NETWORK = 36;
|
||||
private static final int EVENT_REPORT_NETWORK_CONNECTIVITY = 36;
|
||||
|
||||
// Handle changes in Private DNS settings.
|
||||
private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
|
||||
@@ -3541,8 +3542,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(mNetId);
|
||||
if (nai == null) return;
|
||||
|
||||
// NetworkMonitor reports the network validation result as a bitmask while
|
||||
// ConnectivityDiagnostics treats this value as an int. Convert the result to a single
|
||||
// logical value for ConnectivityDiagnostics.
|
||||
final int validationResult = networkMonitorValidationResultToConnDiagsValidationResult(
|
||||
p.result);
|
||||
|
||||
final PersistableBundle extras = new PersistableBundle();
|
||||
extras.putInt(KEY_NETWORK_VALIDATION_RESULT, p.result);
|
||||
extras.putInt(KEY_NETWORK_VALIDATION_RESULT, validationResult);
|
||||
extras.putInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, p.probesSucceeded);
|
||||
extras.putInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK, p.probesAttempted);
|
||||
|
||||
@@ -3618,6 +3625,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given NetworkMonitor-specific validation result bitmask to a
|
||||
* ConnectivityDiagnostics-specific validation result int.
|
||||
*/
|
||||
private int networkMonitorValidationResultToConnDiagsValidationResult(int validationResult) {
|
||||
if ((validationResult & NETWORK_VALIDATION_RESULT_SKIPPED) != 0) {
|
||||
return ConnectivityReport.NETWORK_VALIDATION_RESULT_SKIPPED;
|
||||
}
|
||||
if ((validationResult & NETWORK_VALIDATION_RESULT_VALID) == 0) {
|
||||
return ConnectivityReport.NETWORK_VALIDATION_RESULT_INVALID;
|
||||
}
|
||||
return (validationResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0
|
||||
? ConnectivityReport.NETWORK_VALIDATION_RESULT_PARTIALLY_VALID
|
||||
: ConnectivityReport.NETWORK_VALIDATION_RESULT_VALID;
|
||||
}
|
||||
|
||||
private void notifyDataStallSuspected(DataStallReportParcelable p, int netId) {
|
||||
log("Data stall detected with methods: " + p.detectionMethod);
|
||||
|
||||
@@ -4867,8 +4890,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
|
||||
break;
|
||||
}
|
||||
case EVENT_REVALIDATE_NETWORK: {
|
||||
handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2));
|
||||
case EVENT_REPORT_NETWORK_CONNECTIVITY: {
|
||||
handleReportNetworkConnectivity((NetworkAgentInfo) msg.obj, msg.arg1,
|
||||
toBool(msg.arg2));
|
||||
break;
|
||||
}
|
||||
case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
|
||||
@@ -5031,41 +5055,34 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
final int uid = mDeps.getCallingUid();
|
||||
final int connectivityInfo = encodeBool(hasConnectivity);
|
||||
|
||||
// Handle ConnectivityDiagnostics event before attempting to revalidate the network. This
|
||||
// forces an ordering of ConnectivityDiagnostics events in the case where hasConnectivity
|
||||
// does not match the known connectivity of the network - this causes NetworkMonitor to
|
||||
// revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
|
||||
final NetworkAgentInfo nai;
|
||||
if (network == null) {
|
||||
nai = getDefaultNetwork();
|
||||
} else {
|
||||
nai = getNetworkAgentInfoForNetwork(network);
|
||||
}
|
||||
if (nai != null) {
|
||||
mConnectivityDiagnosticsHandler.sendMessage(
|
||||
mConnectivityDiagnosticsHandler.obtainMessage(
|
||||
ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
|
||||
connectivityInfo, 0, nai));
|
||||
}
|
||||
|
||||
mHandler.sendMessage(
|
||||
mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
|
||||
mHandler.obtainMessage(
|
||||
EVENT_REPORT_NETWORK_CONNECTIVITY, uid, connectivityInfo, nai));
|
||||
}
|
||||
|
||||
private void handleReportNetworkConnectivity(
|
||||
Network network, int uid, boolean hasConnectivity) {
|
||||
final NetworkAgentInfo nai;
|
||||
if (network == null) {
|
||||
nai = getDefaultNetwork();
|
||||
} else {
|
||||
nai = getNetworkAgentInfoForNetwork(network);
|
||||
}
|
||||
if (nai == null || nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTING ||
|
||||
nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTED) {
|
||||
@Nullable NetworkAgentInfo nai, int uid, boolean hasConnectivity) {
|
||||
// TODO(b/192611346): remove NetworkInfo.State.DISCONNECTING as it's not used
|
||||
if (nai == null
|
||||
|| nai != getNetworkAgentInfoForNetwork(nai.network)
|
||||
|| nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTING
|
||||
|| nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTED) {
|
||||
return;
|
||||
}
|
||||
// Revalidate if the app report does not match our current validated state.
|
||||
if (hasConnectivity == nai.lastValidated) {
|
||||
mConnectivityDiagnosticsHandler.sendMessage(
|
||||
mConnectivityDiagnosticsHandler.obtainMessage(
|
||||
ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
|
||||
new ReportedNetworkConnectivityInfo(
|
||||
hasConnectivity, false /* isNetworkRevalidating */, uid, nai)));
|
||||
return;
|
||||
}
|
||||
if (DBG) {
|
||||
@@ -5081,6 +5098,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
if (isNetworkWithCapabilitiesBlocked(nc, uid, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send CONNECTIVITY_REPORTED event before re-validating the Network to force an ordering of
|
||||
// ConnDiags events. This ensures that #onNetworkConnectivityReported() will be called
|
||||
// before #onConnectivityReportAvailable(), which is called once Network evaluation is
|
||||
// completed.
|
||||
mConnectivityDiagnosticsHandler.sendMessage(
|
||||
mConnectivityDiagnosticsHandler.obtainMessage(
|
||||
ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
|
||||
new ReportedNetworkConnectivityInfo(
|
||||
hasConnectivity, true /* isNetworkRevalidating */, uid, nai)));
|
||||
nai.networkMonitor().forceReevaluation(uid);
|
||||
}
|
||||
|
||||
@@ -9039,8 +9066,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
* the platform. This event will invoke {@link
|
||||
* IConnectivityDiagnosticsCallback#onNetworkConnectivityReported} for permissioned
|
||||
* callbacks.
|
||||
* obj = Network that was reported on
|
||||
* arg1 = boolint for the quality reported
|
||||
* obj = ReportedNetworkConnectivityInfo with info on reported Network connectivity.
|
||||
*/
|
||||
private static final int EVENT_NETWORK_CONNECTIVITY_REPORTED = 5;
|
||||
|
||||
@@ -9078,7 +9104,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
break;
|
||||
}
|
||||
case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
|
||||
handleNetworkConnectivityReported((NetworkAgentInfo) msg.obj, toBool(msg.arg1));
|
||||
handleNetworkConnectivityReported((ReportedNetworkConnectivityInfo) msg.obj);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -9148,6 +9174,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used for sending info for a call to {@link #reportNetworkConnectivity()} to {@link
|
||||
* ConnectivityDiagnosticsHandler}.
|
||||
*/
|
||||
private static class ReportedNetworkConnectivityInfo {
|
||||
public final boolean hasConnectivity;
|
||||
public final boolean isNetworkRevalidating;
|
||||
public final int reporterUid;
|
||||
@NonNull public final NetworkAgentInfo nai;
|
||||
|
||||
private ReportedNetworkConnectivityInfo(
|
||||
boolean hasConnectivity,
|
||||
boolean isNetworkRevalidating,
|
||||
int reporterUid,
|
||||
@NonNull NetworkAgentInfo nai) {
|
||||
this.hasConnectivity = hasConnectivity;
|
||||
this.isNetworkRevalidating = isNetworkRevalidating;
|
||||
this.reporterUid = reporterUid;
|
||||
this.nai = nai;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRegisterConnectivityDiagnosticsCallback(
|
||||
@NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
|
||||
ensureRunningOnConnectivityServiceThread();
|
||||
@@ -9255,13 +9303,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
networkCapabilities,
|
||||
extras);
|
||||
nai.setConnectivityReport(report);
|
||||
|
||||
final List<IConnectivityDiagnosticsCallback> results =
|
||||
getMatchingPermissionedCallbacks(nai);
|
||||
getMatchingPermissionedCallbacks(nai, Process.INVALID_UID);
|
||||
for (final IConnectivityDiagnosticsCallback cb : results) {
|
||||
try {
|
||||
cb.onConnectivityReportAvailable(report);
|
||||
} catch (RemoteException ex) {
|
||||
loge("Error invoking onConnectivityReport", ex);
|
||||
loge("Error invoking onConnectivityReportAvailable", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9280,7 +9329,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
networkCapabilities,
|
||||
extras);
|
||||
final List<IConnectivityDiagnosticsCallback> results =
|
||||
getMatchingPermissionedCallbacks(nai);
|
||||
getMatchingPermissionedCallbacks(nai, Process.INVALID_UID);
|
||||
for (final IConnectivityDiagnosticsCallback cb : results) {
|
||||
try {
|
||||
cb.onDataStallSuspected(report);
|
||||
@@ -9291,15 +9340,39 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
|
||||
private void handleNetworkConnectivityReported(
|
||||
@NonNull NetworkAgentInfo nai, boolean connectivity) {
|
||||
@NonNull ReportedNetworkConnectivityInfo reportedNetworkConnectivityInfo) {
|
||||
final NetworkAgentInfo nai = reportedNetworkConnectivityInfo.nai;
|
||||
final ConnectivityReport cachedReport = nai.getConnectivityReport();
|
||||
|
||||
// If the Network is being re-validated as a result of this call to
|
||||
// reportNetworkConnectivity(), notify all permissioned callbacks. Otherwise, only notify
|
||||
// permissioned callbacks registered by the reporter.
|
||||
final List<IConnectivityDiagnosticsCallback> results =
|
||||
getMatchingPermissionedCallbacks(nai);
|
||||
getMatchingPermissionedCallbacks(
|
||||
nai,
|
||||
reportedNetworkConnectivityInfo.isNetworkRevalidating
|
||||
? Process.INVALID_UID
|
||||
: reportedNetworkConnectivityInfo.reporterUid);
|
||||
|
||||
for (final IConnectivityDiagnosticsCallback cb : results) {
|
||||
try {
|
||||
cb.onNetworkConnectivityReported(nai.network, connectivity);
|
||||
cb.onNetworkConnectivityReported(
|
||||
nai.network, reportedNetworkConnectivityInfo.hasConnectivity);
|
||||
} catch (RemoteException ex) {
|
||||
loge("Error invoking onNetworkConnectivityReported", ex);
|
||||
}
|
||||
|
||||
// If the Network isn't re-validating, also provide the cached report. If there is no
|
||||
// cached report, the Network is still being validated and a report will be sent once
|
||||
// validation is complete. Note that networks which never undergo validation will still
|
||||
// have a cached ConnectivityReport with RESULT_SKIPPED.
|
||||
if (!reportedNetworkConnectivityInfo.isNetworkRevalidating && cachedReport != null) {
|
||||
try {
|
||||
cb.onConnectivityReportAvailable(cachedReport);
|
||||
} catch (RemoteException ex) {
|
||||
loge("Error invoking onConnectivityReportAvailable", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9312,20 +9385,38 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of ConnectivityDiagnostics callbacks that match the specified Network and uid.
|
||||
*
|
||||
* <p>If Process.INVALID_UID is specified, all matching callbacks will be returned.
|
||||
*/
|
||||
private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
|
||||
@NonNull NetworkAgentInfo nai) {
|
||||
@NonNull NetworkAgentInfo nai, int uid) {
|
||||
final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
|
||||
for (Entry<IBinder, ConnectivityDiagnosticsCallbackInfo> entry :
|
||||
mConnectivityDiagnosticsCallbacks.entrySet()) {
|
||||
final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
|
||||
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
|
||||
|
||||
// Connectivity Diagnostics rejects multilayer requests at registration hence get(0).
|
||||
if (nai.satisfies(nri.mRequests.get(0))) {
|
||||
if (checkConnectivityDiagnosticsPermissions(
|
||||
nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
|
||||
results.add(entry.getValue().mCb);
|
||||
}
|
||||
if (!nai.satisfies(nri.mRequests.get(0))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// UID for this callback must either be:
|
||||
// - INVALID_UID (which sends callbacks to all UIDs), or
|
||||
// - The callback's owner (the owner called reportNetworkConnectivity() and is being
|
||||
// notified as a result)
|
||||
if (uid != Process.INVALID_UID && uid != nri.mUid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!checkConnectivityDiagnosticsPermissions(
|
||||
nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
results.add(entry.getValue().mCb);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -10197,6 +10197,9 @@ public class ConnectivityServiceTest {
|
||||
public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception {
|
||||
setUpConnectivityDiagnosticsCallback();
|
||||
|
||||
// reset to ignore callbacks from setup
|
||||
reset(mConnectivityDiagnosticsCallback);
|
||||
|
||||
final Network n = mCellNetworkAgent.getNetwork();
|
||||
final boolean hasConnectivity = true;
|
||||
mService.reportNetworkConnectivity(n, hasConnectivity);
|
||||
@@ -10207,6 +10210,8 @@ public class ConnectivityServiceTest {
|
||||
// Verify onNetworkConnectivityReported fired
|
||||
verify(mConnectivityDiagnosticsCallback)
|
||||
.onNetworkConnectivityReported(eq(n), eq(hasConnectivity));
|
||||
verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
|
||||
argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
|
||||
|
||||
final boolean noConnectivity = false;
|
||||
mService.reportNetworkConnectivity(n, noConnectivity);
|
||||
@@ -10217,6 +10222,54 @@ public class ConnectivityServiceTest {
|
||||
// Wait for onNetworkConnectivityReported to fire
|
||||
verify(mConnectivityDiagnosticsCallback)
|
||||
.onNetworkConnectivityReported(eq(n), eq(noConnectivity));
|
||||
|
||||
// Also expect a ConnectivityReport after NetworkMonitor asynchronously re-validates
|
||||
verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS).times(2))
|
||||
.onConnectivityReportAvailable(
|
||||
argThat(report ->
|
||||
areConnDiagCapsRedacted(report.getNetworkCapabilities())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectivityDiagnosticsCallbackOnConnectivityReportedSeparateUid()
|
||||
throws Exception {
|
||||
setUpConnectivityDiagnosticsCallback();
|
||||
|
||||
// reset to ignore callbacks from setup
|
||||
reset(mConnectivityDiagnosticsCallback);
|
||||
|
||||
// report known Connectivity from a different uid. Verify that network is not re-validated
|
||||
// and this callback is not notified.
|
||||
final Network n = mCellNetworkAgent.getNetwork();
|
||||
final boolean hasConnectivity = true;
|
||||
doAsUid(Process.myUid() + 1, () -> mService.reportNetworkConnectivity(n, hasConnectivity));
|
||||
|
||||
// Block until all other events are done processing.
|
||||
HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
|
||||
|
||||
// Verify onNetworkConnectivityReported did not fire
|
||||
verify(mConnectivityDiagnosticsCallback, never())
|
||||
.onNetworkConnectivityReported(any(), anyBoolean());
|
||||
verify(mConnectivityDiagnosticsCallback, never())
|
||||
.onConnectivityReportAvailable(any());
|
||||
|
||||
// report different Connectivity from a different uid. Verify that network is re-validated
|
||||
// and that this callback is notified.
|
||||
final boolean noConnectivity = false;
|
||||
doAsUid(Process.myUid() + 1, () -> mService.reportNetworkConnectivity(n, noConnectivity));
|
||||
|
||||
// Block until all other events are done processing.
|
||||
HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
|
||||
|
||||
// Wait for onNetworkConnectivityReported to fire
|
||||
verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
|
||||
.onNetworkConnectivityReported(eq(n), eq(noConnectivity));
|
||||
|
||||
// Also expect a ConnectivityReport after NetworkMonitor asynchronously re-validates
|
||||
verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
|
||||
.onConnectivityReportAvailable(
|
||||
argThat(report ->
|
||||
areConnDiagCapsRedacted(report.getNetworkCapabilities())));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user