diff options
6 files changed, 301 insertions, 14 deletions
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 43ea5891d7f7..ff4bf2d3474c 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; @@ -418,7 +419,16 @@ public abstract class NetworkAgent extends Handler { if (score < 0) { throw new IllegalArgumentException("Score must be >= 0"); } - queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, score, 0); + final NetworkScore ns = new NetworkScore(); + ns.putIntExtension(NetworkScore.LEGACY_SCORE, score); + updateScore(ns); + } + + /** + * Called by the bearer code when it has a new NetworkScore for this network. + */ + public void updateScore(@NonNull NetworkScore ns) { + queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new NetworkScore(ns)); } /** diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java new file mode 100644 index 000000000000..1ab63352401c --- /dev/null +++ b/core/java/android/net/NetworkScore.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Object representing the quality of a network as perceived by the user. + * + * A NetworkScore object represents the characteristics of a network that affects how good the + * network is considered for a particular use. + * @hide + */ +public final class NetworkScore implements Parcelable { + + // The key of bundle which is used to get the legacy network score of NetworkAgentInfo. + // TODO: Remove this when the transition to NetworkScore is over. + public static final String LEGACY_SCORE = "LEGACY_SCORE"; + @NonNull + private final Bundle mExtensions; + + public NetworkScore() { + mExtensions = new Bundle(); + } + + public NetworkScore(@NonNull NetworkScore source) { + mExtensions = new Bundle(source.mExtensions); + } + + /** + * Put the value of parcelable inside the bundle by key. + */ + public void putExtension(@Nullable String key, @Nullable Parcelable value) { + mExtensions.putParcelable(key, value); + } + + /** + * Put the value of int inside the bundle by key. + */ + public void putIntExtension(@Nullable String key, int value) { + mExtensions.putInt(key, value); + } + + /** + * Get the value of non primitive type by key. + */ + public <T extends Parcelable> T getExtension(@Nullable String key) { + return mExtensions.getParcelable(key); + } + + /** + * Get the value of int by key. + */ + public int getIntExtension(@Nullable String key) { + return mExtensions.getInt(key); + } + + /** + * Remove the entry by given key. + */ + public void removeExtension(@Nullable String key) { + mExtensions.remove(key); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + synchronized (this) { + dest.writeBundle(mExtensions); + } + } + + public static final @NonNull Creator<NetworkScore> CREATOR = new Creator<NetworkScore>() { + @Override + public NetworkScore createFromParcel(@NonNull Parcel in) { + return new NetworkScore(in); + } + + @Override + public NetworkScore[] newArray(int size) { + return new NetworkScore[size]; + } + }; + + private NetworkScore(@NonNull Parcel in) { + mExtensions = in.readBundle(); + } + + // TODO: Modify this method once new fields are added into this class. + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof NetworkScore)) { + return false; + } + final NetworkScore other = (NetworkScore) obj; + return bundlesEqual(mExtensions, other.mExtensions); + } + + @Override + public int hashCode() { + int result = 29; + for (String key : mExtensions.keySet()) { + final Object value = mExtensions.get(key); + // The key may be null, so call Objects.hash() is safer. + result += 31 * value.hashCode() + 37 * Objects.hash(key); + } + return result; + } + + // mExtensions won't be null since the constructor will create it. + private boolean bundlesEqual(@NonNull Bundle bundle1, @NonNull Bundle bundle2) { + if (bundle1 == bundle2) { + return true; + } + + // This is unlikely but it's fine to add this clause here. + if (null == bundle1 || null == bundle2) { + return false; + } + + if (bundle1.size() != bundle2.size()) { + return false; + } + + for (String key : bundle1.keySet()) { + final Object value1 = bundle1.get(key); + final Object value2 = bundle2.get(key); + if (!Objects.equals(value1, value2)) { + return false; + } + } + return true; + } +} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ce0e9e7e56cf..81eb4b355a21 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -97,6 +97,7 @@ import android.net.NetworkMonitorManager; import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; @@ -2643,7 +2644,8 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: { - updateNetworkScore(nai, msg.arg1); + final NetworkScore ns = (NetworkScore) msg.obj; + updateNetworkScore(nai, ns); break; } case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: { @@ -5594,9 +5596,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network // satisfies mDefaultRequest. final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); + final NetworkScore ns = new NetworkScore(); + ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore); final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, - currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, + ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver, mNMS, factorySerialNumber); // Make sure the network capabilities reflect what the agent info says. nai.setNetworkCapabilities(mixInCapabilities(nai, nc)); @@ -6729,7 +6733,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void updateNetworkScore(NetworkAgentInfo nai, int score) { + private void updateNetworkScore(NetworkAgentInfo nai, NetworkScore ns) { + int score = ns.getIntExtension(NetworkScore.LEGACY_SCORE); if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + score); if (score < 0) { loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score + @@ -6738,7 +6743,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } final int oldScore = nai.getCurrentScore(); - nai.setCurrentScore(score); + nai.setNetworkScore(ns); rematchAllNetworksAndRequests(nai, oldScore); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 96b7cb315f58..24a5b7fa1489 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -28,6 +28,7 @@ import android.net.NetworkInfo; import android.net.NetworkMisc; import android.net.NetworkMonitorManager; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkState; import android.os.Handler; import android.os.INetworkManagementService; @@ -227,8 +228,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // validated). private boolean mLingering; - // This represents the last score received from the NetworkAgent. - private int currentScore; + // This represents the characteristics of a network that affects how good the network is + // considered for a particular use. + @NonNull + private NetworkScore mNetworkScore; // The list of NetworkRequests being satisfied by this Network. private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>(); @@ -257,8 +260,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private final Handler mHandler; public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, - LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, - NetworkMisc misc, ConnectivityService connService, INetd netd, + LinkProperties lp, NetworkCapabilities nc, @NonNull NetworkScore ns, Context context, + Handler handler, NetworkMisc misc, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) { this.messenger = messenger; asyncChannel = ac; @@ -266,7 +269,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { networkInfo = info; linkProperties = lp; networkCapabilities = nc; - currentScore = score; + mNetworkScore = ns; clatd = new Nat464Xlat(this, netd, dnsResolver, nms); mConnService = connService; mContext = context; @@ -483,7 +486,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE; } - int score = currentScore; + int score = mNetworkScore.getIntExtension(NetworkScore.LEGACY_SCORE); if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) { score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; } @@ -512,8 +515,13 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return getCurrentScore(true); } - public void setCurrentScore(int newScore) { - currentScore = newScore; + public void setNetworkScore(@NonNull NetworkScore ns) { + mNetworkScore = ns; + } + + @NonNull + public NetworkScore getNetworkScore() { + return mNetworkScore; } public NetworkState getNetworkState() { diff --git a/tests/net/common/java/android/net/NetworkScoreTest.kt b/tests/net/common/java/android/net/NetworkScoreTest.kt new file mode 100644 index 000000000000..30836b7c9be1 --- /dev/null +++ b/tests/net/common/java/android/net/NetworkScoreTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net + +import android.os.Parcelable +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +private const val TEST_SCORE = 80 +private const val KEY_DEFAULT_CAPABILITIES = "DEFAULT_CAPABILITIES" + +@RunWith(AndroidJUnit4::class) +@SmallTest +class NetworkScoreTest { + @Test + fun testParcelNetworkScore() { + val networkScore = NetworkScore() + val defaultCap = NetworkCapabilities() + networkScore.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap) + assertEquals(defaultCap, networkScore.getExtension(KEY_DEFAULT_CAPABILITIES)) + networkScore.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE) + assertEquals(TEST_SCORE, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE)) + assertParcelSane(networkScore, 1) + } + + @Test + fun testNullKeyAndValue() { + val networkScore = NetworkScore() + val defaultCap = NetworkCapabilities() + networkScore.putIntExtension(null, TEST_SCORE) + assertEquals(TEST_SCORE, networkScore.getIntExtension(null)) + networkScore.putExtension(null, defaultCap) + assertEquals(defaultCap, networkScore.getExtension(null)) + networkScore.putExtension(null, null) + val result: Parcelable? = networkScore.getExtension(null) + assertEquals(null, result) + } + + @Test + fun testRemoveExtension() { + val networkScore = NetworkScore() + val defaultCap = NetworkCapabilities() + networkScore.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap) + networkScore.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE) + assertEquals(defaultCap, networkScore.getExtension(KEY_DEFAULT_CAPABILITIES)) + assertEquals(TEST_SCORE, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE)) + networkScore.removeExtension(KEY_DEFAULT_CAPABILITIES) + networkScore.removeExtension(NetworkScore.LEGACY_SCORE) + val result: Parcelable? = networkScore.getExtension(KEY_DEFAULT_CAPABILITIES) + assertEquals(null, result) + assertEquals(0, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE)) + } + + @Test + fun testEqualsNetworkScore() { + val ns1 = NetworkScore() + val ns2 = NetworkScore() + assertTrue(ns1.equals(ns2)) + assertEquals(ns1.hashCode(), ns2.hashCode()) + + ns1.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE) + assertFalse(ns1.equals(ns2)) + assertNotEquals(ns1.hashCode(), ns2.hashCode()) + ns2.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE) + assertTrue(ns1.equals(ns2)) + assertEquals(ns1.hashCode(), ns2.hashCode()) + + val defaultCap = NetworkCapabilities() + ns1.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap) + assertFalse(ns1.equals(ns2)) + assertNotEquals(ns1.hashCode(), ns2.hashCode()) + ns2.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap) + assertTrue(ns1.equals(ns2)) + assertEquals(ns1.hashCode(), ns2.hashCode()) + + ns1.putIntExtension(null, 10) + assertFalse(ns1.equals(ns2)) + assertNotEquals(ns1.hashCode(), ns2.hashCode()) + ns2.putIntExtension(null, 10) + assertTrue(ns1.equals(ns2)) + assertEquals(ns1.hashCode(), ns2.hashCode()) + } +} diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 142769f61335..535298f9b09a 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -39,6 +39,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkMisc; +import android.net.NetworkScore; import android.os.INetworkManagementService; import android.text.format.DateUtils; @@ -354,8 +355,10 @@ public class LingerMonitorTest { NetworkCapabilities caps = new NetworkCapabilities(); caps.addCapability(0); caps.addTransportType(transport); + NetworkScore ns = new NetworkScore(); + ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50); NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null, - caps, 50, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS, + caps, ns, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS, NetworkFactory.SerialNumber.NONE); nai.everValidated = true; return nai; |