Simplify and correct isNetworkPotentialSatisfier.

This makes the code clearer, and also fixes a bug: currently, a
TRACK_DEFAULT request in a multilayer request can cause a network
to remain needed. This is incorrect: only requests should be able
to keep a network up.

This prevents satellite networking from disconnecting when the
default network reappears (see changes in unit test).

This CL removes the code before the loop that special-cases
non-multilayer listens because it is not very useful. Any
non-multilayer listen will either:
- Immediately return false, if it's the active request, or:
- Cause the code to continue (because it's not the active
  request), and because mRequests only has one element, will
  hit the "return false" statement after the loop.

Flag: EXEMPT bug fix
Test: covered by existing tests
Change-Id: I7295ce469335af924aa20b7976cc4a9136ef5d61
This commit is contained in:
Lorenzo Colitti 2025-04-21 11:14:56 +09:00
commit dd577efdde
2 changed files with 44 additions and 49 deletions

View file

@ -6018,46 +6018,48 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// For example, declaring it a potential satisfier would keep an unvalidated destroyed
// candidate after it's been replaced by another unvalidated network.
if (candidate.isDestroyed()) return false;
// Listen requests won't keep up a network satisfying it. If this is not a multilayer
// request, return immediately. For multilayer requests, check to see if any of the
// multilayer requests may have a potential satisfier.
if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen()
|| nri.mRequests.get(0).isListenForBest())) {
return false;
}
for (final NetworkRequest req : nri.mRequests) {
// This multilayer listen request is satisfied therefore no further requests need to be
// evaluated deeming this network not a potential satisfier.
if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) {
// If the active request is of a type that does not cause the network to be kept up,
// the network cannot be a potential satisfier: even if there are requests after this
// request in the list, they cannot become the active request, and therefore, cannot
// cause this network to be kept up.
// Note that this determination can only be made after evaluating previous requests in
// the array, because those requests might make the network a potential satisfier even
// if they are not currently active (e.g., they might currently be satisfied by another
// network with a higher score than this one).
if (!req.isRequest() && nri.getActiveRequest() == req) {
return false;
}
// As non-multilayer listen requests have already returned, the below would only happen
// for a multilayer request therefore continue to the next request if available.
if (req.isListen() || req.isListenForBest()) {
continue;
}
// If there is hope for this network might validate and subsequently become the best
// network for that request, then it is needed. Note that this network can't already
// be the best for this request, or it would be the current satisfier, and therefore
// there would be no need to call this method to find out if it is a *potential*
// satisfier ("unneeded", the only caller, only calls this if this network currently
// satisfies no request).
if (candidate.satisfies(req)) {
// As soon as a network is found that satisfies a request, return. Specifically for
// multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
// is important so as to not evaluate lower priority requests further in
// nri.mRequests.
final NetworkAgentInfo champion = req.equals(nri.getActiveRequest())
? nri.getSatisfier() : null;
// Note that this catches two important cases:
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
// is currently satisfying the request. This is desirable when
// cellular ends up validating but WiFi does not.
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
return mNetworkRanker.mightBeat(req, champion, candidate.getValidatedScoreable());
}
// This request type does not cause the network to be kept up and therefore does not
// cause the network to be a potential satisfier. Later requests in the list might.
if (!req.isRequest()) continue;
// The network can never be a potential satisfier for the request because it does not
// satisfy it. Later requests in the list might.
if (!candidate.satisfies(req)) continue;
// This request could cause the network to be kept up if and only if it could become the
// best network for the request by validating.
// Note that this network can't already be the best for this request, or it would be the
// current satisfier. Because this code is only called if the candidate network is
// satisfying zero requests, there are only two cases here:
// - The request is not being satisfied by any network (i.e., getActiveRequest() is not
// this request).
// - The request is satisfied by another network and this network might (or might not)
// beat it by validating.
// Note that this catches two important cases:
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
// is currently satisfying the request. This is desirable when
// cellular ends up validating but WiFi does not.
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
if (req != nri.getActiveRequest()) return true;
final NetworkAgentInfo champion = nri.getSatisfier();
return mNetworkRanker.mightBeat(req, champion, candidate.getValidatedScoreable());
}
return false;

View file

@ -271,10 +271,8 @@ class CSSatelliteNetworkTest : CSTest() {
allNetworksCb.expectAvailableCallbacks(wifiNetwork, validated = false)
defaultCb.expectAvailableCallbacks(wifiNetwork, validated = false)
otherUidCb.expectAvailableCallbacks(wifiNetwork, validated = false)
// BUG: satellite should disconnect because there are no requests for it.
// cb.expect<Losing>(satelliteNetwork)
// cb.expect<Lost>(satelliteNetwork)
allNetworksCb.assertNoCallback()
allNetworksCb.expect<Losing>(satelliteNetwork)
allNetworksCb.expect<Lost>(satelliteNetwork)
wifiAgent.disconnect()
allNetworksCb.expect<Lost>(wifiNetwork)
@ -290,17 +288,12 @@ class CSSatelliteNetworkTest : CSTest() {
val satelliteNetwork2 = satelliteAgent2.network
allNetworksCb.expectAvailableCallbacks(satelliteNetwork2, validated = false)
// BUG: this should be satelliteNetwork2, because satelliteNetwork should have gone down.
defaultCb.expectAvailableCallbacks(satelliteNetwork, validated = false)
defaultCb.expectAvailableCallbacks(satelliteNetwork2, validated = false)
updateSatelliteNetworkFallbackUids(setOf())
// BUG: satelliteNetwork2 should disconnect because there are no requests for it.
// cb.expect<Lost>(satelliteNetwork2)
// defaultCb.expect<Lost>(satelliteNetwork2)
allNetworksCb.expect<Lost>(satelliteNetwork)
defaultCb.expect<Lost>(satelliteNetwork)
allNetworksCb.expect<Lost>(satelliteNetwork2)
defaultCb.expect<Lost>(satelliteNetwork2)
otherUidCb.assertNoCallback()
}