Merge "Add UpstreamNetworkState for tethering"

This commit is contained in:
Treehugger Robot
2019-12-24 05:33:15 +00:00
committed by Gerrit Code Review
7 changed files with 113 additions and 76 deletions

View File

@@ -21,7 +21,6 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkState;
import android.net.RouteInfo;
import android.net.ip.IpServer;
import android.net.util.NetworkConstants;
@@ -72,7 +71,7 @@ public class IPv6TetheringCoordinator {
private final LinkedList<Downstream> mActiveDownstreams;
private final byte[] mUniqueLocalPrefix;
private short mNextSubnetId;
private NetworkState mUpstreamNetworkState;
private UpstreamNetworkState mUpstreamNetworkState;
public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
mNotifyList = notifyList;
@@ -115,11 +114,11 @@ public class IPv6TetheringCoordinator {
}
/**
* Call when upstream NetworkState may be changed.
* If upstream has ipv6 for tethering, update this new NetworkState
* Call when UpstreamNetworkState may be changed.
* If upstream has ipv6 for tethering, update this new UpstreamNetworkState
* to IpServer. Otherwise stop ipv6 tethering on downstream interfaces.
*/
public void updateUpstreamNetworkState(NetworkState ns) {
public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
@@ -144,18 +143,15 @@ public class IPv6TetheringCoordinator {
}
}
private void setUpstreamNetworkState(NetworkState ns) {
private void setUpstreamNetworkState(UpstreamNetworkState ns) {
if (ns == null) {
mUpstreamNetworkState = null;
} else {
// Make a deep copy of the parts we need.
mUpstreamNetworkState = new NetworkState(
null,
mUpstreamNetworkState = new UpstreamNetworkState(
new LinkProperties(ns.linkProperties),
new NetworkCapabilities(ns.networkCapabilities),
new Network(ns.network),
null,
null);
new Network(ns.network));
}
mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
@@ -295,14 +291,11 @@ public class IPv6TetheringCoordinator {
return in6addr;
}
private static String toDebugString(NetworkState ns) {
private static String toDebugString(UpstreamNetworkState ns) {
if (ns == null) {
return "NetworkState{null}";
return "UpstreamNetworkState{null}";
}
return String.format("NetworkState{%s, %s, %s}",
ns.network,
ns.networkCapabilities,
ns.linkProperties);
return ns.toString();
}
private static void stopIPv6TetheringOn(IpServer ipServer) {

View File

@@ -70,7 +70,6 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.TetherStatesParcel;
import android.net.TetheringConfigurationParcel;
@@ -1152,7 +1151,7 @@ public class Tethering {
// Needed because the canonical source of upstream truth is just the
// upstream interface set, |mCurrentUpstreamIfaceSet|.
private boolean pertainsToCurrentUpstream(NetworkState ns) {
private boolean pertainsToCurrentUpstream(UpstreamNetworkState ns) {
if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
@@ -1320,7 +1319,7 @@ public class Tethering {
maybeDunSettingChanged();
final TetheringConfiguration config = mConfig;
final NetworkState ns = (config.chooseUpstreamAutomatically)
final UpstreamNetworkState ns = (config.chooseUpstreamAutomatically)
? mUpstreamNetworkMonitor.getCurrentPreferredUpstream()
: mUpstreamNetworkMonitor.selectPreferredUpstreamType(
config.preferredUpstreamIfaceTypes);
@@ -1341,7 +1340,7 @@ public class Tethering {
}
}
protected void setUpstreamNetwork(NetworkState ns) {
protected void setUpstreamNetwork(UpstreamNetworkState ns) {
InterfaceSet ifaces = null;
if (ns != null) {
// Find the interface with the default IPv4 route. It may be the
@@ -1357,7 +1356,7 @@ public class Tethering {
}
notifyDownstreamsOfNewUpstreamIface(ifaces);
if (ns != null && pertainsToCurrentUpstream(ns)) {
// If we already have NetworkState for this network update it immediately.
// If we already have UpstreamNetworkState for this network update it immediately.
handleNewUpstreamNetworkState(ns);
} else if (mCurrentUpstreamIfaceSet == null) {
// There are no available upstream networks.
@@ -1394,7 +1393,7 @@ public class Tethering {
}
}
protected void handleNewUpstreamNetworkState(NetworkState ns) {
protected void handleNewUpstreamNetworkState(UpstreamNetworkState ns) {
mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
mOffload.updateUpstreamNetworkState(ns);
}
@@ -1458,7 +1457,7 @@ public class Tethering {
return;
}
final NetworkState ns = (NetworkState) o;
final UpstreamNetworkState ns = (UpstreamNetworkState) o;
if (ns == null || !pertainsToCurrentUpstream(ns)) {
// TODO: In future, this is where upstream evaluation and selection
@@ -1728,7 +1727,7 @@ public class Tethering {
mOffloadController.stop();
}
public void updateUpstreamNetworkState(NetworkState ns) {
public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
mOffloadController.setUpstreamLinkProperties(
(ns != null) ? ns.linkProperties : null);
}

View File

@@ -19,7 +19,6 @@ package com.android.server.connectivity.tethering;
import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.NetworkState;
import android.net.RouteInfo;
import android.net.util.InterfaceSet;
@@ -35,7 +34,7 @@ public final class TetheringInterfaceUtils {
* Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
* @return null if there is no usable interface, or a set of at least one interface otherwise.
*/
public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
public static @Nullable InterfaceSet getTetheringInterfaces(UpstreamNetworkState ns) {
if (ns == null) {
return null;
}
@@ -51,7 +50,7 @@ public final class TetheringInterfaceUtils {
* Get the upstream interface for IPv6 tethering.
* @return null if there is no usable interface, or the interface name otherwise.
*/
public static @Nullable String getIPv6Interface(NetworkState ns) {
public static @Nullable String getIPv6Interface(UpstreamNetworkState ns) {
// Broadly speaking:
//
// [1] does the upstream have an IPv6 default route?

View File

@@ -32,7 +32,6 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -90,7 +89,7 @@ public class UpstreamNetworkMonitor {
private final StateMachine mTarget;
private final Handler mHandler;
private final int mWhat;
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
private final HashMap<Network, UpstreamNetworkState> mNetworkMap = new HashMap<>();
private HashSet<IpPrefix> mLocalPrefixes;
private ConnectivityManager mCM;
private EntitlementManager mEntitlementMgr;
@@ -236,7 +235,7 @@ public class UpstreamNetworkMonitor {
/**
* Select the first available network from |perferredTypes|.
*/
public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
public UpstreamNetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
@@ -274,8 +273,8 @@ public class UpstreamNetworkMonitor {
* preferred upstream would be DUN otherwise preferred upstream is the same as default network.
* Returns null if no current upstream is available.
*/
public NetworkState getCurrentPreferredUpstream() {
final NetworkState dfltState = (mDefaultInternetNetwork != null)
public UpstreamNetworkState getCurrentPreferredUpstream() {
final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
? mNetworkMap.get(mDefaultInternetNetwork)
: null;
if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
@@ -312,11 +311,11 @@ public class UpstreamNetworkMonitor {
if (mNetworkMap.containsKey(network)) return;
if (VDBG) Log.d(TAG, "onAvailable for " + network);
mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
mNetworkMap.put(network, new UpstreamNetworkState(null, null, network));
}
private void handleNetCap(Network network, NetworkCapabilities newNc) {
final NetworkState prev = mNetworkMap.get(network);
final UpstreamNetworkState prev = mNetworkMap.get(network);
if (prev == null || newNc.equals(prev.networkCapabilities)) {
// Ignore notifications about networks for which we have not yet
// received onAvailable() (should never happen) and any duplicate
@@ -336,15 +335,15 @@ public class UpstreamNetworkMonitor {
mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
}
mNetworkMap.put(network, new NetworkState(
null, prev.linkProperties, newNc, network, null, null));
mNetworkMap.put(network, new UpstreamNetworkState(
prev.linkProperties, newNc, network));
// TODO: If sufficient information is available to select a more
// preferable upstream, do so now and notify the target.
notifyTarget(EVENT_ON_CAPABILITIES, network);
}
private void handleLinkProp(Network network, LinkProperties newLp) {
final NetworkState prev = mNetworkMap.get(network);
final UpstreamNetworkState prev = mNetworkMap.get(network);
if (prev == null || newLp.equals(prev.linkProperties)) {
// Ignore notifications about networks for which we have not yet
// received onAvailable() (should never happen) and any duplicate
@@ -357,8 +356,8 @@ public class UpstreamNetworkMonitor {
network, newLp));
}
mNetworkMap.put(network, new NetworkState(
null, newLp, prev.networkCapabilities, network, null, null));
mNetworkMap.put(network, new UpstreamNetworkState(
newLp, prev.networkCapabilities, network));
// TODO: If sufficient information is available to select a more
// preferable upstream, do so now and notify the target.
notifyTarget(EVENT_ON_LINKPROPERTIES, network);
@@ -509,11 +508,11 @@ public class UpstreamNetworkMonitor {
private static class TypeStatePair {
public int type = TYPE_NONE;
public NetworkState ns = null;
public UpstreamNetworkState ns = null;
}
private static TypeStatePair findFirstAvailableUpstreamByType(
Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes,
Iterable<UpstreamNetworkState> netStates, Iterable<Integer> preferredTypes,
boolean isCellularUpstreamPermitted) {
final TypeStatePair result = new TypeStatePair();
@@ -532,7 +531,7 @@ public class UpstreamNetworkMonitor {
nc.setSingleUid(Process.myUid());
for (NetworkState value : netStates) {
for (UpstreamNetworkState value : netStates) {
if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
continue;
}
@@ -546,10 +545,10 @@ public class UpstreamNetworkMonitor {
return result;
}
private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) {
private static HashSet<IpPrefix> allLocalPrefixes(Iterable<UpstreamNetworkState> netStates) {
final HashSet<IpPrefix> prefixSet = new HashSet<>();
for (NetworkState ns : netStates) {
for (UpstreamNetworkState ns : netStates) {
final LinkProperties lp = ns.linkProperties;
if (lp == null) continue;
prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp));
@@ -563,7 +562,7 @@ public class UpstreamNetworkMonitor {
return Integer.toString(nc.getSignalStrength());
}
private static boolean isCellular(NetworkState ns) {
private static boolean isCellular(UpstreamNetworkState ns) {
return (ns != null) && isCellular(ns.networkCapabilities);
}
@@ -572,18 +571,19 @@ public class UpstreamNetworkMonitor {
&& nc.hasCapability(NET_CAPABILITY_NOT_VPN);
}
private static boolean hasCapability(NetworkState ns, int netCap) {
private static boolean hasCapability(UpstreamNetworkState ns, int netCap) {
return (ns != null) && (ns.networkCapabilities != null)
&& ns.networkCapabilities.hasCapability(netCap);
}
private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
private static boolean isNetworkUsableAndNotCellular(UpstreamNetworkState ns) {
return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null)
&& !isCellular(ns.networkCapabilities);
}
private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
for (NetworkState ns : netStates) {
private static UpstreamNetworkState findFirstDunNetwork(
Iterable<UpstreamNetworkState> netStates) {
for (UpstreamNetworkState ns : netStates) {
if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
}

View File

@@ -0,0 +1,51 @@
/*
* 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 com.android.server.connectivity.tethering;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import androidx.annotation.NonNull;
/**
* Snapshot of tethering upstream network state.
*/
public class UpstreamNetworkState {
/** {@link LinkProperties}. */
public final LinkProperties linkProperties;
/** {@link NetworkCapabilities}. */
public final NetworkCapabilities networkCapabilities;
/** {@link Network}. */
public final Network network;
/** Constructs a new UpstreamNetworkState. */
public UpstreamNetworkState(LinkProperties linkProperties,
NetworkCapabilities networkCapabilities, Network network) {
this.linkProperties = linkProperties;
this.networkCapabilities = networkCapabilities;
this.network = network;
}
@NonNull
@Override
public String toString() {
return String.format("UpstreamNetworkState{%s, %s, %s}",
network == null ? "null" : network,
networkCapabilities == null ? "null" : networkCapabilities,
linkProperties == null ? "null" : linkProperties);
}
}

View File

@@ -27,7 +27,6 @@ import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
@@ -82,7 +81,6 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.TetherStatesParcel;
@@ -360,10 +358,8 @@ public class TetheringTest {
}
}
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
boolean with464xlat) {
final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, 0, null, null);
info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
boolean withIPv6, boolean with464xlat) {
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_MOBILE_IFNAME);
@@ -393,22 +389,22 @@ public class TetheringTest {
final NetworkCapabilities capabilities = new NetworkCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
return new UpstreamNetworkState(prop, capabilities, new Network(100));
}
private static NetworkState buildMobileIPv4UpstreamState() {
private static UpstreamNetworkState buildMobileIPv4UpstreamState() {
return buildMobileUpstreamState(true, false, false);
}
private static NetworkState buildMobileIPv6UpstreamState() {
private static UpstreamNetworkState buildMobileIPv6UpstreamState() {
return buildMobileUpstreamState(false, true, false);
}
private static NetworkState buildMobileDualStackUpstreamState() {
private static UpstreamNetworkState buildMobileDualStackUpstreamState() {
return buildMobileUpstreamState(true, true, false);
}
private static NetworkState buildMobile464xlatUpstreamState() {
private static UpstreamNetworkState buildMobile464xlatUpstreamState() {
return buildMobileUpstreamState(false, true, true);
}
@@ -562,7 +558,7 @@ public class TetheringTest {
verifyNoMoreInteractions(mWifiManager);
}
private void prepareUsbTethering(NetworkState upstreamState) {
private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
@@ -577,7 +573,7 @@ public class TetheringTest {
@Test
public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
NetworkState upstreamState = buildMobileIPv4UpstreamState();
UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
prepareUsbTethering(upstreamState);
// This should produce no activity of any kind.
@@ -657,14 +653,14 @@ public class TetheringTest {
/**
* Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator.
*/
private void sendIPv6TetherUpdates(NetworkState upstreamState) {
private void sendIPv6TetherUpdates(UpstreamNetworkState upstreamState) {
// IPv6TetheringCoordinator must have been notified of downstream
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
eq(IpServer.STATE_TETHERED));
for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) {
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
UpstreamNetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIpv6Provisioned()
? ipv6OnlyState.linkProperties
@@ -673,7 +669,7 @@ public class TetheringTest {
mLooper.dispatchAll();
}
private void runUsbTethering(NetworkState upstreamState) {
private void runUsbTethering(UpstreamNetworkState upstreamState) {
prepareUsbTethering(upstreamState);
sendUsbBroadcast(true, true, true);
mLooper.dispatchAll();
@@ -681,7 +677,7 @@ public class TetheringTest {
@Test
public void workingMobileUsbTethering_IPv4() throws Exception {
NetworkState upstreamState = buildMobileIPv4UpstreamState();
UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -696,7 +692,7 @@ public class TetheringTest {
public void workingMobileUsbTethering_IPv4LegacyDhcp() {
Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
sendConfigurationChanged();
final NetworkState upstreamState = buildMobileIPv4UpstreamState();
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
sendIPv6TetherUpdates(upstreamState);
@@ -705,7 +701,7 @@ public class TetheringTest {
@Test
public void workingMobileUsbTethering_IPv6() throws Exception {
NetworkState upstreamState = buildMobileIPv6UpstreamState();
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -718,7 +714,7 @@ public class TetheringTest {
@Test
public void workingMobileUsbTethering_DualStack() throws Exception {
NetworkState upstreamState = buildMobileDualStackUpstreamState();
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -733,7 +729,7 @@ public class TetheringTest {
@Test
public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
NetworkState upstreamState = buildMobile464xlatUpstreamState();
UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
@@ -751,7 +747,7 @@ public class TetheringTest {
@Test
public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
// Setup IPv6
NetworkState upstreamState = buildMobileIPv6UpstreamState();
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -789,7 +785,7 @@ public class TetheringTest {
sendConfigurationChanged();
// Setup IPv6
final NetworkState upstreamState = buildMobileIPv6UpstreamState();
final UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
// UpstreamNetworkMonitor should choose upstream automatically
@@ -1172,7 +1168,7 @@ public class TetheringTest {
TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
assertEquals(tetherState, null);
// 2. Enable wifi tethering.
NetworkState upstreamState = buildMobileDualStackUpstreamState();
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);

View File

@@ -50,7 +50,6 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Message;
@@ -539,7 +538,7 @@ public class UpstreamNetworkMonitorTest {
mUNM.selectPreferredUpstreamType(preferredTypes));
verify(mEntitleMgr, times(1)).maybeRunProvisioning();
}
private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) {
if (legacyType == TYPE_NONE) {
assertTrue(ns == null);
return;