Replace StringNetworkSpecifier & subId with TelephonyNetworkSpecifer

StringNetworkSpecifier is widely used to specify subscription id for
the NetworkRequest. The raw string field introduce ambiguity and leave
large space for bugs. With TelephonyNetworkSpecifer, we are able to
settle down the type and value of the fields (although currently only
one) and introduce validation to detect the bug in the beginning.

Bug: 145993724
Test: atest FrameworksNetTests FrameworksTelephonyTests &
      make offline-sdk-docs
Change-Id: Iefbad9b1deb3de2c0b262d9ce5ae0704a50d08a6
This commit is contained in:
Rambo Wang
2019-12-12 18:16:10 -08:00
parent 6408fa473f
commit 818cf71da8
10 changed files with 306 additions and 45 deletions

View File

@@ -29167,7 +29167,7 @@ package android.net {
method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
}
@@ -29266,6 +29266,19 @@ package android.net {
method public void onStopped();
}
public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
method public int describeContents();
method public int getSubscriptionId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.TelephonyNetworkSpecifier> CREATOR;
}
public static final class TelephonyNetworkSpecifier.Builder {
ctor public TelephonyNetworkSpecifier.Builder();
method @NonNull public android.net.TelephonyNetworkSpecifier build();
method @NonNull public android.net.TelephonyNetworkSpecifier.Builder setSubscriptionId(int);
}
public class TrafficStats {
ctor public TrafficStats();
method public static void clearThreadStatsTag();

View File

@@ -4638,6 +4638,10 @@ package android.net {
field @NonNull public final String specifier;
}
public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
method public boolean satisfiedBy(android.net.NetworkSpecifier);
}
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();

View File

@@ -300,22 +300,34 @@ public class NetworkRequest implements Parcelable {
* this without a single transport set will generate an exception, as will
* subsequently adding or removing transports after this is set.
* </p>
* The interpretation of this {@code String} is bearer specific and bearers that use
* it should document their particulars. For example, Bluetooth may use some sort of
* device id while WiFi could used ssid and/or bssid. Cellular may use carrier spn.
* If the {@code networkSpecifier} is provided, it shall be interpreted as follows:
* <ul>
* <li>If the specifier can be parsed as an integer, it will be treated as a
* {@link android.net TelephonyNetworkSpecifier}, and the provided integer will be
* interpreted as a SubscriptionId.
* <li>If the value is an ethernet interface name, it will be treated as such.
* <li>For all other cases, the behavior is undefined.
* </ul>
*
* @param networkSpecifier An {@code String} of opaque format used to specify the bearer
* specific network specifier where the bearer has a choice of
* networks.
* @param networkSpecifier A {@code String} of either a SubscriptionId in cellular
* network request or an ethernet interface name in ethernet
* network request.
*
* @deprecated Use {@link #setNetworkSpecifier(NetworkSpecifier)} instead.
*/
@Deprecated
public Builder setNetworkSpecifier(String networkSpecifier) {
/*
* A StringNetworkSpecifier does not accept null or empty ("") strings. When network
* specifiers were strings a null string and an empty string were considered equivalent.
* Hence no meaning is attached to a null or empty ("") string.
*/
return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
: new StringNetworkSpecifier(networkSpecifier));
try {
int subId = Integer.parseInt(networkSpecifier);
return setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(subId).build());
} catch (NumberFormatException nfe) {
// A StringNetworkSpecifier does not accept null or empty ("") strings. When network
// specifiers were strings a null string and an empty string were considered
// equivalent. Hence no meaning is attached to a null or empty ("") string.
return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
: new StringNetworkSpecifier(networkSpecifier));
}
}
/**

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2020 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.os.Parcel;
import android.os.Parcelable;
/**
* NetworkSpecifier object for cellular network request. Apps should use the
* {@link TelephonyNetworkSpecifier.Builder} class to create an instance.
*/
public final class TelephonyNetworkSpecifier extends NetworkSpecifier implements Parcelable {
private final int mSubId;
/**
* Return the subscription Id of current TelephonyNetworkSpecifier object.
*
* @return The subscription id.
*/
public int getSubscriptionId() {
return mSubId;
}
/**
* @hide
*/
public TelephonyNetworkSpecifier(int subId) {
this.mSubId = subId;
}
public static final @NonNull Creator<TelephonyNetworkSpecifier> CREATOR =
new Creator<TelephonyNetworkSpecifier>() {
@Override
public TelephonyNetworkSpecifier createFromParcel(Parcel in) {
int subId = in.readInt();
return new TelephonyNetworkSpecifier(subId);
}
@Override
public TelephonyNetworkSpecifier[] newArray(int size) {
return new TelephonyNetworkSpecifier[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mSubId);
}
@Override
public int hashCode() {
return mSubId;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof TelephonyNetworkSpecifier)) {
return false;
}
TelephonyNetworkSpecifier lhs = (TelephonyNetworkSpecifier) obj;
return mSubId == lhs.mSubId;
}
@Override
public String toString() {
return new StringBuilder()
.append("TelephonyNetworkSpecifier [")
.append("mSubId = ").append(mSubId)
.append("]")
.toString();
}
/** @hide */
@Override
public boolean satisfiedBy(NetworkSpecifier other) {
// Any generic requests should be satisfied by a specific telephony network.
// For simplicity, we treat null same as MatchAllNetworkSpecifier
return equals(other) || other == null || other instanceof MatchAllNetworkSpecifier;
}
/**
* Builder to create {@link TelephonyNetworkSpecifier} object.
*/
public static final class Builder {
// Integer.MIN_VALUE which is not a valid subId, services as the sentinel to check if
// subId was set
private static final int SENTINEL_SUB_ID = Integer.MIN_VALUE;
private int mSubId;
public Builder() {
mSubId = SENTINEL_SUB_ID;
}
/**
* Set the subscription id.
*
* @param subId The subscription Id.
* @return Instance of {@link Builder} to enable the chaining of the builder method.
*/
public @NonNull Builder setSubscriptionId(int subId) {
mSubId = subId;
return this;
}
/**
* Create a NetworkSpecifier for the cellular network request.
*
* @return TelephonyNetworkSpecifier object.
* @throws IllegalArgumentException when subscription Id is not provided through
* {@link #setSubscriptionId(int)}.
*/
public @NonNull TelephonyNetworkSpecifier build() {
if (mSubId == SENTINEL_SUB_ID) {
throw new IllegalArgumentException("Subscription Id is not provided.");
}
return new TelephonyNetworkSpecifier(mSubId);
}
}
}

View File

@@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
@@ -46,19 +47,18 @@ import android.net.NetworkIdentity;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.net.Uri;
import android.os.BestClock;
import android.os.Handler;
import android.os.SystemClock;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.DataUnit;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.Range;
import android.util.Slog;
@@ -74,7 +74,6 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -185,7 +184,6 @@ public class MultipathPolicyTracker {
// Track information on mobile networks as they come and go.
class MultipathTracker {
final Network network;
final int subId;
final String subscriberId;
private long mQuota;
@@ -198,13 +196,14 @@ public class MultipathPolicyTracker {
public MultipathTracker(Network network, NetworkCapabilities nc) {
this.network = network;
this.mNetworkCapabilities = new NetworkCapabilities(nc);
try {
subId = Integer.parseInt(
((StringNetworkSpecifier) nc.getNetworkSpecifier()).toString());
} catch (ClassCastException | NullPointerException | NumberFormatException e) {
NetworkSpecifier specifier = nc.getNetworkSpecifier();
int subId = INVALID_SUBSCRIPTION_ID;
if (specifier instanceof TelephonyNetworkSpecifier) {
subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
} else {
throw new IllegalStateException(String.format(
"Can't get subId from mobile network %s (%s): %s",
network, nc, e.getMessage()));
"Can't get subId from mobile network %s (%s)",
network, nc));
}
TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);

View File

@@ -28,7 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.NetworkSpecifier;
import android.net.StringNetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;
import android.os.UserHandle;
import android.telephony.SubscriptionManager;
@@ -223,14 +223,8 @@ public class NetworkNotificationManager {
// name has been added to it
NetworkSpecifier specifier = nai.networkCapabilities.getNetworkSpecifier();
int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
if (specifier instanceof StringNetworkSpecifier) {
try {
subId = Integer.parseInt(
((StringNetworkSpecifier) specifier).specifier);
} catch (NumberFormatException e) {
Slog.e(TAG, "NumberFormatException on "
+ ((StringNetworkSpecifier) specifier).specifier);
}
if (specifier instanceof TelephonyNetworkSpecifier) {
subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
}
details = mTelephonyManager.createForSubscriptionId(subId)

View File

@@ -161,7 +161,7 @@ import android.net.NetworkStack;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.net.TrafficStats;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
@@ -5304,16 +5304,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
private int parseSubId(NetworkState state) {
// TODO: moved to using a legitimate NetworkSpecifier instead of string parsing
int subId = INVALID_SUBSCRIPTION_ID;
if (state != null && state.networkCapabilities != null
&& state.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier();
if (spec instanceof StringNetworkSpecifier) {
try {
subId = Integer.parseInt(((StringNetworkSpecifier) spec).specifier);
} catch (NumberFormatException e) {
}
if (spec instanceof TelephonyNetworkSpecifier) {
subId = ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
}
}
return subId;

View File

@@ -112,7 +112,7 @@ import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.os.Binder;
import android.os.Handler;
import android.os.INetworkManagementService;
@@ -1836,7 +1836,8 @@ public class NetworkPolicyManagerServiceTest {
if (!roaming) {
nc.addCapability(NET_CAPABILITY_NOT_ROAMING);
}
nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subId)));
nc.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(subId).build());
return nc;
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2018 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 static com.android.testutils.ParcelUtilsKt.assertParcelSane;
import static org.junit.Assert.assertEquals;
import android.telephony.SubscriptionManager;
import androidx.test.filters.SmallTest;
import org.junit.Test;
/**
* Unit test for {@link android.net.TelephonyNetworkSpecifier}.
*/
@SmallTest
public class TelephonyNetworkSpecifierTest {
private static final int TEST_SUBID = 5;
/**
* Validate that IllegalArgumentException will be thrown if build TelephonyNetworkSpecifier
* without calling {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}.
*/
@Test
public void testBuilderBuildWithDefault() {
try {
new TelephonyNetworkSpecifier.Builder().build();
} catch (IllegalArgumentException iae) {
// expected, test pass
}
}
/**
* Validate that no exception will be thrown even if pass invalid subscription id to
* {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}.
*/
@Test
public void testBuilderBuildWithInvalidSubId() {
TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.build();
assertEquals(specifier.getSubscriptionId(), SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
/**
* Validate the correctness of TelephonyNetworkSpecifier when provide valid subId.
*/
@Test
public void testBuilderBuildWithValidSubId() {
final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(TEST_SUBID)
.build();
assertEquals(TEST_SUBID, specifier.getSubscriptionId());
}
/**
* Validate that parcel marshalling/unmarshalling works.
*/
@Test
public void testParcel() {
TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(TEST_SUBID)
.build();
assertParcelSane(specifier, 1 /* fieldCount */);
}
}

View File

@@ -51,6 +51,7 @@ import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -229,7 +230,7 @@ public class MultipathPolicyTrackerTest {
verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any());
// Simulate callback after capability changes
final NetworkCapabilities capabilities = new NetworkCapabilities()
NetworkCapabilities capabilities = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
.addTransportType(TRANSPORT_CELLULAR)
.setNetworkSpecifier(new StringNetworkSpecifier("234"));
@@ -239,6 +240,19 @@ public class MultipathPolicyTrackerTest {
networkCallback.getValue().onCapabilitiesChanged(
TEST_NETWORK,
capabilities);
// make sure it also works with the new introduced TelephonyNetworkSpecifier
capabilities = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
.addTransportType(TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(234).build());
if (!roaming) {
capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
}
networkCallback.getValue().onCapabilitiesChanged(
TEST_NETWORK,
capabilities);
}
@Test