[NS06] Implement the don't-reap mechanism
This exposes a mechanism for network providers to tell the network stack that a given network must be kept up for some specific reason. This is meant to be easier for them than to have to file a request, in particular because there is no guaranteed way to make sure the request will be best matched by any given network. Test: new test for this Bug: 167544279 Merged-In: I3c2563d4ae4e3715d0c6270344ba8f7ef067872f Merged-In: I238a3ee5ee9262477a23b897e4141769dd1505d1 Change-Id: I238a3ee5ee9262477a23b897e4141769dd1505d1 (cherry-picked from ag/13929760)
This commit is contained in:
@@ -341,14 +341,18 @@ package android.net {
|
||||
|
||||
public final class NetworkScore implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public int getKeepConnectedReason();
|
||||
method public int getLegacyInt();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
|
||||
field public static final int KEEP_CONNECTED_FOR_HANDOVER = 1; // 0x1
|
||||
field public static final int KEEP_CONNECTED_NONE = 0; // 0x0
|
||||
}
|
||||
|
||||
public static final class NetworkScore.Builder {
|
||||
ctor public NetworkScore.Builder();
|
||||
method @NonNull public android.net.NetworkScore build();
|
||||
method @NonNull public android.net.NetworkScore.Builder setKeepConnectedReason(int);
|
||||
method @NonNull public android.net.NetworkScore.Builder setLegacyInt(int);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
@@ -23,6 +24,9 @@ import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Object representing the quality of a network as perceived by the user.
|
||||
*
|
||||
@@ -36,6 +40,17 @@ public final class NetworkScore implements Parcelable {
|
||||
// a migration.
|
||||
private final int mLegacyInt;
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(value = {
|
||||
KEEP_CONNECTED_NONE,
|
||||
KEEP_CONNECTED_FOR_HANDOVER
|
||||
})
|
||||
public @interface KeepConnectedReason { }
|
||||
|
||||
public static final int KEEP_CONNECTED_NONE = 0;
|
||||
public static final int KEEP_CONNECTED_FOR_HANDOVER = 1;
|
||||
|
||||
// Agent-managed policies
|
||||
// TODO : add them here, starting from 1
|
||||
/** @hide */
|
||||
@@ -46,21 +61,33 @@ public final class NetworkScore implements Parcelable {
|
||||
// Bitmask of all the policies applied to this score.
|
||||
private final long mPolicies;
|
||||
|
||||
private final int mKeepConnectedReason;
|
||||
|
||||
/** @hide */
|
||||
NetworkScore(final int legacyInt, final long policies) {
|
||||
NetworkScore(final int legacyInt, final long policies,
|
||||
@KeepConnectedReason final int keepConnectedReason) {
|
||||
mLegacyInt = legacyInt;
|
||||
mPolicies = policies;
|
||||
mKeepConnectedReason = keepConnectedReason;
|
||||
}
|
||||
|
||||
private NetworkScore(@NonNull final Parcel in) {
|
||||
mLegacyInt = in.readInt();
|
||||
mPolicies = in.readLong();
|
||||
mKeepConnectedReason = in.readInt();
|
||||
}
|
||||
|
||||
public int getLegacyInt() {
|
||||
return mLegacyInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keep-connected reason, or KEEP_CONNECTED_NONE.
|
||||
*/
|
||||
public int getKeepConnectedReason() {
|
||||
return mKeepConnectedReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this score has a particular policy.
|
||||
*
|
||||
@@ -80,6 +107,7 @@ public final class NetworkScore implements Parcelable {
|
||||
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
|
||||
dest.writeInt(mLegacyInt);
|
||||
dest.writeLong(mPolicies);
|
||||
dest.writeInt(mKeepConnectedReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,6 +136,7 @@ public final class NetworkScore implements Parcelable {
|
||||
private static final long POLICY_NONE = 0L;
|
||||
private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
|
||||
private int mLegacyInt = INVALID_LEGACY_INT;
|
||||
private int mKeepConnectedReason = KEEP_CONNECTED_NONE;
|
||||
|
||||
/**
|
||||
* Sets the legacy int for this score.
|
||||
@@ -123,13 +152,24 @@ public final class NetworkScore implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the keep-connected reason.
|
||||
*
|
||||
* This can be reset by calling it again with {@link KEEP_CONNECTED_NONE}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setKeepConnectedReason(@KeepConnectedReason final int reason) {
|
||||
mKeepConnectedReason = reason;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds this NetworkScore.
|
||||
* @return The built NetworkScore object.
|
||||
*/
|
||||
@NonNull
|
||||
public NetworkScore build() {
|
||||
return new NetworkScore(mLegacyInt, POLICY_NONE);
|
||||
return new NetworkScore(mLegacyInt, POLICY_NONE, mKeepConnectedReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3974,6 +3974,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
// then it should be lingered.
|
||||
private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
|
||||
ensureRunningOnConnectivityServiceThread();
|
||||
|
||||
if (!nai.everConnected || nai.isVPN() || nai.isInactive()
|
||||
|| nai.getScore().getKeepConnectedReason() != NetworkScore.KEEP_CONNECTED_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int numRequests;
|
||||
switch (reason) {
|
||||
case TEARDOWN:
|
||||
@@ -3987,9 +3993,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
|
||||
return false;
|
||||
}
|
||||
if (numRequests > 0) return false;
|
||||
|
||||
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
|
||||
if (reason == UnneededFor.LINGER
|
||||
&& !nri.isMultilayerRequest()
|
||||
|
||||
@@ -19,12 +19,14 @@ package com.android.server.connectivity;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||
import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.net.NetworkAgentConfig;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkScore;
|
||||
import android.net.NetworkScore.KeepConnectedReason;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
@@ -95,9 +97,13 @@ public class FullScore {
|
||||
// Bitmask of all the policies applied to this score.
|
||||
private final long mPolicies;
|
||||
|
||||
FullScore(final int legacyInt, final long policies) {
|
||||
private final int mKeepConnectedReason;
|
||||
|
||||
FullScore(final int legacyInt, final long policies,
|
||||
@KeepConnectedReason final int keepConnectedReason) {
|
||||
mLegacyInt = legacyInt;
|
||||
mPolicies = policies;
|
||||
mKeepConnectedReason = keepConnectedReason;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,14 +116,15 @@ public class FullScore {
|
||||
*/
|
||||
public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
|
||||
@NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config) {
|
||||
return withPolicies(score.getLegacyInt(), caps.hasCapability(NET_CAPABILITY_VALIDATED),
|
||||
return withPolicies(score.getLegacyInt(), score.getKeepConnectedReason(),
|
||||
caps.hasCapability(NET_CAPABILITY_VALIDATED),
|
||||
caps.hasTransport(TRANSPORT_VPN),
|
||||
config.explicitlySelected,
|
||||
config.acceptUnvalidated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a score supplied by the NetworkAgent, produce a prospective score for an offer.
|
||||
* Given a score supplied by a NetworkProvider, produce a prospective score for an offer.
|
||||
*
|
||||
* NetworkOffers have score filters that are compared to the scores of actual networks
|
||||
* to see if they could possibly beat the current satisfier. Some things the agent can't
|
||||
@@ -139,8 +146,8 @@ public class FullScore {
|
||||
final boolean everUserSelected = false;
|
||||
// Don't assume the user will accept unvalidated connectivity.
|
||||
final boolean acceptUnvalidated = false;
|
||||
return withPolicies(score.getLegacyInt(), mayValidate, vpn, everUserSelected,
|
||||
acceptUnvalidated);
|
||||
return withPolicies(score.getLegacyInt(), KEEP_CONNECTED_NONE,
|
||||
mayValidate, vpn, everUserSelected, acceptUnvalidated);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,13 +159,15 @@ public class FullScore {
|
||||
*/
|
||||
public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
|
||||
@NonNull final NetworkAgentConfig config) {
|
||||
return withPolicies(mLegacyInt, caps.hasCapability(NET_CAPABILITY_VALIDATED),
|
||||
return withPolicies(mLegacyInt, mKeepConnectedReason,
|
||||
caps.hasCapability(NET_CAPABILITY_VALIDATED),
|
||||
caps.hasTransport(TRANSPORT_VPN),
|
||||
config.explicitlySelected,
|
||||
config.acceptUnvalidated);
|
||||
}
|
||||
|
||||
private static FullScore withPolicies(@NonNull final int legacyInt,
|
||||
@KeepConnectedReason final int keepConnectedReason,
|
||||
final boolean isValidated,
|
||||
final boolean isVpn,
|
||||
final boolean everUserSelected,
|
||||
@@ -167,7 +176,8 @@ public class FullScore {
|
||||
(isValidated ? 1L << POLICY_IS_VALIDATED : 0)
|
||||
| (isVpn ? 1L << POLICY_IS_VPN : 0)
|
||||
| (everUserSelected ? 1L << POLICY_EVER_USER_SELECTED : 0)
|
||||
| (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0));
|
||||
| (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0),
|
||||
keepConnectedReason);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,13 +229,21 @@ public class FullScore {
|
||||
return 0 != (mPolicies & (1L << policy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keep-connected reason, or KEEP_CONNECTED_NONE.
|
||||
*/
|
||||
public int getKeepConnectedReason() {
|
||||
return mKeepConnectedReason;
|
||||
}
|
||||
|
||||
// Example output :
|
||||
// Score(50 ; Policies : EVER_USER_SELECTED&IS_VALIDATED)
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringJoiner sj = new StringJoiner(
|
||||
"&", // delimiter
|
||||
"Score(" + mLegacyInt + " ; Policies : ", // prefix
|
||||
"Score(" + mLegacyInt + " ; KeepConnected : " + mKeepConnectedReason
|
||||
+ " ; Policies : ", // prefix
|
||||
")"); // suffix
|
||||
for (int i = NetworkScore.MIN_AGENT_MANAGED_POLICY;
|
||||
i <= NetworkScore.MAX_AGENT_MANAGED_POLICY; ++i) {
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.net.NetworkAgent;
|
||||
import android.net.NetworkAgentConfig;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkProvider;
|
||||
import android.net.NetworkScore;
|
||||
import android.net.NetworkSpecifier;
|
||||
import android.net.QosFilter;
|
||||
import android.net.SocketKeepalive;
|
||||
@@ -199,6 +200,11 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
public void setScore(@NonNull final NetworkScore score) {
|
||||
mScore = score.getLegacyInt();
|
||||
mNetworkAgent.sendNetworkScore(score);
|
||||
}
|
||||
|
||||
public void adjustScore(int change) {
|
||||
mScore += change;
|
||||
mNetworkAgent.sendNetworkScore(mScore);
|
||||
|
||||
@@ -104,6 +104,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
|
||||
import static android.net.NetworkScore.KEEP_CONNECTED_FOR_HANDOVER;
|
||||
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
|
||||
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
|
||||
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
|
||||
@@ -10226,6 +10227,83 @@ public class ConnectivityServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepConnected() throws Exception {
|
||||
setAlwaysOnNetworks(false);
|
||||
registerDefaultNetworkCallbacks();
|
||||
final TestNetworkCallback allNetworksCb = new TestNetworkCallback();
|
||||
final NetworkRequest allNetworksRequest = new NetworkRequest.Builder().clearCapabilities()
|
||||
.build();
|
||||
mCm.registerNetworkCallback(allNetworksRequest, allNetworksCb);
|
||||
|
||||
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
|
||||
mCellNetworkAgent.connect(true /* validated */);
|
||||
|
||||
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
|
||||
allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
|
||||
|
||||
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
|
||||
mWiFiNetworkAgent.connect(true /* validated */);
|
||||
|
||||
mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
|
||||
// While the default callback doesn't see the network before it's validated, the listen
|
||||
// sees the network come up and validate later
|
||||
allNetworksCb.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
|
||||
allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
|
||||
allNetworksCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
|
||||
allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
|
||||
TEST_LINGER_DELAY_MS * 2);
|
||||
|
||||
// The cell network has disconnected (see LOST above) because it was outscored and
|
||||
// had no requests (see setAlwaysOnNetworks(false) above)
|
||||
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
|
||||
final NetworkScore score = new NetworkScore.Builder().setLegacyInt(30).build();
|
||||
mCellNetworkAgent.setScore(score);
|
||||
mCellNetworkAgent.connect(false /* validated */);
|
||||
|
||||
// The cell network gets torn down right away.
|
||||
allNetworksCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
|
||||
allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
|
||||
TEST_NASCENT_DELAY_MS * 2);
|
||||
allNetworksCb.assertNoCallback();
|
||||
|
||||
// Now create a cell network with KEEP_CONNECTED_FOR_HANDOVER and make sure it's
|
||||
// not disconnected immediately when outscored.
|
||||
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
|
||||
final NetworkScore scoreKeepup = new NetworkScore.Builder().setLegacyInt(30)
|
||||
.setKeepConnectedReason(KEEP_CONNECTED_FOR_HANDOVER).build();
|
||||
mCellNetworkAgent.setScore(scoreKeepup);
|
||||
mCellNetworkAgent.connect(true /* validated */);
|
||||
|
||||
allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
|
||||
mDefaultNetworkCallback.assertNoCallback();
|
||||
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
|
||||
allNetworksCb.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
|
||||
mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
|
||||
mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
|
||||
|
||||
// Reconnect a WiFi network and make sure the cell network is still not torn down.
|
||||
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
|
||||
mWiFiNetworkAgent.connect(true /* validated */);
|
||||
|
||||
allNetworksCb.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
|
||||
mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
|
||||
|
||||
// Now remove the reason to keep connected and make sure the network lingers and is
|
||||
// torn down.
|
||||
mCellNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).build());
|
||||
allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent,
|
||||
TEST_NASCENT_DELAY_MS * 2);
|
||||
allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
|
||||
TEST_LINGER_DELAY_MS * 2);
|
||||
mDefaultNetworkCallback.assertNoCallback();
|
||||
|
||||
mCm.unregisterNetworkCallback(allNetworksCb);
|
||||
// mDefaultNetworkCallback will be unregistered by tearDown()
|
||||
}
|
||||
|
||||
private class QosCallbackMockHelper {
|
||||
@NonNull public final QosFilter mFilter;
|
||||
@NonNull public final IQosCallback mCallback;
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.connectivity
|
||||
|
||||
import android.net.NetworkAgentConfig
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkScore.KEEP_CONNECTED_NONE
|
||||
import android.text.TextUtils
|
||||
import android.util.ArraySet
|
||||
import androidx.test.filters.SmallTest
|
||||
@@ -60,11 +61,11 @@ class FullScoreTest {
|
||||
|
||||
@Test
|
||||
fun testGetLegacyInt() {
|
||||
val ns = FullScore(50, 0L /* policy */)
|
||||
val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE)
|
||||
assertEquals(10, ns.legacyInt) // -40 penalty for not being validated
|
||||
assertEquals(50, ns.legacyIntAsValidated)
|
||||
|
||||
val vpnNs = FullScore(101, 0L /* policy */).withPolicies(vpn = true)
|
||||
val vpnNs = FullScore(101, 0L /* policy */, KEEP_CONNECTED_NONE).withPolicies(vpn = true)
|
||||
assertEquals(101, vpnNs.legacyInt) // VPNs are not subject to unvalidation penalty
|
||||
assertEquals(101, vpnNs.legacyIntAsValidated)
|
||||
assertEquals(101, vpnNs.withPolicies(validated = true).legacyInt)
|
||||
@@ -83,7 +84,7 @@ class FullScoreTest {
|
||||
|
||||
@Test
|
||||
fun testToString() {
|
||||
val string = FullScore(10, 0L /* policy */)
|
||||
val string = FullScore(10, 0L /* policy */, KEEP_CONNECTED_NONE)
|
||||
.withPolicies(vpn = true, acceptUnvalidated = true).toString()
|
||||
assertTrue(string.contains("Score(10"), string)
|
||||
assertTrue(string.contains("ACCEPT_UNVALIDATED"), string)
|
||||
@@ -107,7 +108,7 @@ class FullScoreTest {
|
||||
|
||||
@Test
|
||||
fun testHasPolicy() {
|
||||
val ns = FullScore(50, 0L /* policy */)
|
||||
val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE)
|
||||
assertFalse(ns.hasPolicy(POLICY_IS_VALIDATED))
|
||||
assertFalse(ns.hasPolicy(POLICY_IS_VPN))
|
||||
assertFalse(ns.hasPolicy(POLICY_EVER_USER_SELECTED))
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.connectivity
|
||||
import android.net.INetworkOfferCallback
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.net.NetworkScore.KEEP_CONNECTED_NONE
|
||||
import androidx.test.filters.SmallTest
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import org.junit.Test
|
||||
@@ -38,7 +39,7 @@ class NetworkOfferTest {
|
||||
|
||||
@Test
|
||||
fun testOfferNeededUnneeded() {
|
||||
val score = FullScore(50, POLICY_NONE)
|
||||
val score = FullScore(50, POLICY_NONE, KEEP_CONNECTED_NONE)
|
||||
val offer = NetworkOffer(score, NetworkCapabilities.Builder().build(), mockCallback,
|
||||
1 /* providerId */)
|
||||
val request1 = mock(NetworkRequest::class.java)
|
||||
|
||||
Reference in New Issue
Block a user