Merge "Add API for proxy configuration over VPN."

This commit is contained in:
Treehugger Robot
2019-01-24 17:52:22 +00:00
committed by Gerrit Code Review
13 changed files with 204 additions and 99 deletions

View File

@@ -27853,6 +27853,7 @@ package android.net {
method public android.os.ParcelFileDescriptor establish();
method public android.net.VpnService.Builder setBlocking(boolean);
method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent);
method public android.net.VpnService.Builder setHttpProxy(android.net.ProxyInfo);
method public android.net.VpnService.Builder setMtu(int);
method public android.net.VpnService.Builder setSession(String);
method public android.net.VpnService.Builder setUnderlyingNetworks(android.net.Network[]);

View File

@@ -72,9 +72,7 @@ import android.graphics.ImageDecoder;
import android.hardware.display.DisplayManagerGlobal;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.Network;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -1005,15 +1003,10 @@ public final class ActivityThread extends ClientTransactionHandler {
NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
}
public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) {
public void updateHttpProxy() {
final ConnectivityManager cm = ConnectivityManager.from(
getApplication() != null ? getApplication() : getSystemContext());
final Network network = cm.getBoundNetworkForProcess();
if (network != null) {
Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
} else {
Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
}
public void processInBackground() {
@@ -5850,8 +5843,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// crash if we can't get it.
final IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
Proxy.setHttpProxySystemProperty(proxyInfo);
Proxy.setHttpProxySystemProperty(service.getProxyForNetwork(null));
} catch (RemoteException e) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw e.rethrowFromSystemServer();

View File

@@ -99,8 +99,7 @@ oneway interface IApplicationThread {
void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
in String[] args);
void clearDnsCache();
void setHttpProxy(in String proxy, in String port, in String exclList,
in Uri pacFileUrl);
void updateHttpProxy();
void setCoreSettings(in Bundle coreSettings);
void updatePackageCompatibilityInfo(in String pkg, in CompatibilityInfo info);
void scheduleTrimMemory(int level);

View File

@@ -39,12 +39,12 @@ import java.util.Locale;
*/
public class ProxyInfo implements Parcelable {
private String mHost;
private int mPort;
private String mExclusionList;
private String[] mParsedExclusionList;
private final String mHost;
private final int mPort;
private final String mExclusionList;
private final String[] mParsedExclusionList;
private final Uri mPacFileUrl;
private Uri mPacFileUrl;
/**
*@hide
*/
@@ -96,7 +96,8 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(String host, int port, String exclList) {
mHost = host;
mPort = port;
setExclusionList(exclList);
mExclusionList = exclList;
mParsedExclusionList = parseExclusionList(mExclusionList);
mPacFileUrl = Uri.EMPTY;
}
@@ -107,7 +108,8 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(Uri pacFileUrl) {
mHost = LOCAL_HOST;
mPort = LOCAL_PORT;
setExclusionList(LOCAL_EXCL_LIST);
mExclusionList = LOCAL_EXCL_LIST;
mParsedExclusionList = parseExclusionList(mExclusionList);
if (pacFileUrl == null) {
throw new NullPointerException();
}
@@ -121,7 +123,8 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(String pacFileUrl) {
mHost = LOCAL_HOST;
mPort = LOCAL_PORT;
setExclusionList(LOCAL_EXCL_LIST);
mExclusionList = LOCAL_EXCL_LIST;
mParsedExclusionList = parseExclusionList(mExclusionList);
mPacFileUrl = Uri.parse(pacFileUrl);
}
@@ -132,13 +135,22 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
mHost = LOCAL_HOST;
mPort = localProxyPort;
setExclusionList(LOCAL_EXCL_LIST);
mExclusionList = LOCAL_EXCL_LIST;
mParsedExclusionList = parseExclusionList(mExclusionList);
if (pacFileUrl == null) {
throw new NullPointerException();
}
mPacFileUrl = pacFileUrl;
}
private static String[] parseExclusionList(String exclusionList) {
if (exclusionList == null) {
return new String[0];
} else {
return exclusionList.toLowerCase(Locale.ROOT).split(",");
}
}
private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
mHost = host;
mPort = port;
@@ -159,6 +171,10 @@ public class ProxyInfo implements Parcelable {
mExclusionList = source.getExclusionListAsString();
mParsedExclusionList = source.mParsedExclusionList;
} else {
mHost = null;
mPort = 0;
mExclusionList = null;
mParsedExclusionList = null;
mPacFileUrl = Uri.EMPTY;
}
}
@@ -214,24 +230,14 @@ public class ProxyInfo implements Parcelable {
return mExclusionList;
}
// comma separated
private void setExclusionList(String exclusionList) {
mExclusionList = exclusionList;
if (mExclusionList == null) {
mParsedExclusionList = new String[0];
} else {
mParsedExclusionList = exclusionList.toLowerCase(Locale.ROOT).split(",");
}
}
/**
* @hide
*/
public boolean isValid() {
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
mPort == 0 ? "" : Integer.toString(mPort),
mExclusionList == null ? "" : mExclusionList);
mPort == 0 ? "" : Integer.toString(mPort),
mExclusionList == null ? "" : mExclusionList);
}
/**
@@ -262,7 +268,7 @@ public class ProxyInfo implements Parcelable {
sb.append("] ");
sb.append(Integer.toString(mPort));
if (mExclusionList != null) {
sb.append(" xl=").append(mExclusionList);
sb.append(" xl=").append(mExclusionList);
}
} else {
sb.append("[ProxyProperties.mHost == null]");
@@ -308,8 +314,8 @@ public class ProxyInfo implements Parcelable {
*/
public int hashCode() {
return ((null == mHost) ? 0 : mHost.hashCode())
+ ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+ mPort;
+ ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+ mPort;
}
/**
@@ -352,8 +358,7 @@ public class ProxyInfo implements Parcelable {
}
String exclList = in.readString();
String[] parsedExclList = in.readStringArray();
ProxyInfo proxyProperties =
new ProxyInfo(host, port, exclList, parsedExclList);
ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
return proxyProperties;
}

View File

@@ -508,6 +508,15 @@ public class VpnService extends Service {
return this;
}
/**
* Sets an HTTP proxy for the VPN network. This proxy is only a recommendation
* and it is possible that some apps will ignore it.
*/
public Builder setHttpProxy(ProxyInfo proxyInfo) {
mConfig.proxyInfo = proxyInfo;
return this;
}
/**
* Add a network address to the VPN interface. Both IPv4 and IPv6
* addresses are supported. At least one address must be set before

View File

@@ -28,6 +28,7 @@ import android.content.res.Resources;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.Network;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -104,6 +105,7 @@ public class VpnConfig implements Parcelable {
public boolean allowIPv4;
public boolean allowIPv6;
public Network[] underlyingNetworks;
public ProxyInfo proxyInfo;
public void updateAllowedFamilies(InetAddress address) {
if (address instanceof Inet4Address) {
@@ -164,6 +166,7 @@ public class VpnConfig implements Parcelable {
out.writeInt(allowIPv4 ? 1 : 0);
out.writeInt(allowIPv6 ? 1 : 0);
out.writeTypedArray(underlyingNetworks, flags);
out.writeParcelable(proxyInfo, flags);
}
public static final Parcelable.Creator<VpnConfig> CREATOR =
@@ -189,6 +192,7 @@ public class VpnConfig implements Parcelable {
config.allowIPv4 = in.readInt() != 0;
config.allowIPv6 = in.readInt() != 0;
config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
config.proxyInfo = in.readParcelable(null);
return config;
}
@@ -220,6 +224,7 @@ public class VpnConfig implements Parcelable {
.append(", allowIPv4=").append(allowIPv4)
.append(", allowIPv6=").append(allowIPv6)
.append(", underlyingNetworks=").append(Arrays.toString(underlyingNetworks))
.append(", proxyInfo=").append(proxyInfo.toString())
.append("}")
.toString();
}

View File

@@ -32,11 +32,11 @@ public class VpnInfo implements Parcelable {
@Override
public String toString() {
return "VpnInfo{" +
"ownerUid=" + ownerUid +
", vpnIface='" + vpnIface + '\'' +
", primaryUnderlyingIface='" + primaryUnderlyingIface + '\'' +
'}';
return "VpnInfo{"
+ "ownerUid=" + ownerUid
+ ", vpnIface='" + vpnIface + '\''
+ ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\''
+ '}';
}
@Override

View File

@@ -38,7 +38,6 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
@@ -499,7 +498,7 @@ public class TransactionParcelTests {
}
@Override
public void setHttpProxy(String s, String s1, String s2, Uri uri) throws RemoteException {
public void updateHttpProxy() throws RemoteException {
}
@Override

View File

@@ -515,7 +515,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
// the world when it changes.
private final ProxyTracker mProxyTracker;
@VisibleForTesting
protected final ProxyTracker mProxyTracker;
final private SettingsObserver mSettingsObserver;
@@ -824,7 +825,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mPolicyManagerInternal = checkNotNull(
LocalServices.getService(NetworkPolicyManagerInternal.class),
"missing NetworkPolicyManagerInternal");
mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
mProxyTracker = makeProxyTracker();
mNetd = NetdService.getInstance();
mKeyStore = KeyStore.getInstance();
@@ -990,6 +991,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
deps);
}
@VisibleForTesting
protected ProxyTracker makeProxyTracker() {
return new ProxyTracker(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
@@ -3736,20 +3742,46 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
/**
* Returns information about the proxy a certain network is using. If given a null network, it
* it will return the proxy for the bound network for the caller app or the default proxy if
* none.
*
* @param network the network we want to get the proxy information for.
* @return Proxy information if a network has a proxy configured, or otherwise null.
*/
@Override
public ProxyInfo getProxyForNetwork(Network network) {
if (network == null) return mProxyTracker.getDefaultProxy();
final ProxyInfo globalProxy = mProxyTracker.getGlobalProxy();
if (globalProxy != null) return globalProxy;
if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
// caller may not have.
if (network == null) {
// Get the network associated with the calling UID.
final Network activeNetwork = getActiveNetworkForUidInternal(Binder.getCallingUid(),
true);
if (activeNetwork == null) {
return null;
}
return getLinkPropertiesProxyInfo(activeNetwork);
} else if (queryUserAccess(Binder.getCallingUid(), network.netId)) {
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
// caller may not have.
return getLinkPropertiesProxyInfo(network);
}
// No proxy info available if the calling UID does not have network access.
return null;
}
@VisibleForTesting
protected boolean queryUserAccess(int uid, int netId) {
return NetworkUtils.queryUserAccess(uid, netId);
}
private ProxyInfo getLinkPropertiesProxyInfo(Network network) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return null;
synchronized (nai) {
final ProxyInfo proxyInfo = nai.linkProperties.getHttpProxy();
if (proxyInfo == null) return null;
return new ProxyInfo(proxyInfo);
final ProxyInfo linkHttpProxy = nai.linkProperties.getHttpProxy();
return linkHttpProxy == null ? null : new ProxyInfo(linkHttpProxy);
}
}
@@ -3773,11 +3805,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
mProxyTracker.setDefaultProxy(proxy);
}
// If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy.
// This method gets called when any network changes proxy, but the broadcast only ever contains
// the default proxy (even if it hasn't changed).
// TODO: Deprecate the broadcast extras as they aren't necessarily applicable in a multi-network
// world where an app might be bound to a non-default network.
// If the proxy has changed from oldLp to newLp, resend proxy broadcast. This method gets called
// when any network changes proxy.
// TODO: Remove usage of broadcast extras as they are deprecated and not applicable in a
// multi-network world where an app might be bound to a non-default network.
private void updateProxy(LinkProperties newLp, LinkProperties oldLp) {
ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy();
ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
@@ -5975,12 +6006,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
scheduleUnvalidatedPrompt(networkAgent);
if (networkAgent.isVPN()) {
// Temporarily disable the default proxy (not global).
mProxyTracker.setDefaultProxyEnabled(false);
// TODO: support proxy per network.
}
// Whether a particular NetworkRequest listen should cause signal strength thresholds to
// be communicated to a particular NetworkAgent depends only on the network's immutable,
// capabilities, so it only needs to be done once on initial connect, not every time the
@@ -5999,10 +6024,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else if (state == NetworkInfo.State.DISCONNECTED) {
networkAgent.asyncChannel.disconnect();
if (networkAgent.isVPN()) {
mProxyTracker.setDefaultProxyEnabled(true);
updateUids(networkAgent, networkAgent.networkCapabilities, null);
}
disconnectAndDestroyNetwork(networkAgent);
if (networkAgent.isVPN()) {
// As the active or bound network changes for apps, broadcast the default proxy, as
// apps may need to update their proxy data. This is called after disconnecting from
// VPN to make sure we do not broadcast the old proxy data.
// TODO(b/122649188): send the broadcast only to VPN users.
mProxyTracker.sendProxyBroadcast();
}
} else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
state == NetworkInfo.State.SUSPENDED) {
// going into or coming out of SUSPEND: re-score and notify

View File

@@ -2240,17 +2240,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
} break;
case UPDATE_HTTP_PROXY_MSG: {
ProxyInfo proxy = (ProxyInfo)msg.obj;
String host = "";
String port = "";
String exclList = "";
Uri pacFileUrl = Uri.EMPTY;
if (proxy != null) {
host = proxy.getHost();
port = Integer.toString(proxy.getPort());
exclList = proxy.getExclusionListAsString();
pacFileUrl = proxy.getPacFileUrl();
}
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
@@ -2258,7 +2247,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// ConnectivityManager and don't have network privileges anyway.
if (r.thread != null && !r.isolated) {
try {
r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
r.thread.updateHttpProxy();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update http proxy for: " +
r.info.processName);
@@ -21457,8 +21446,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
break;
case Proxy.PROXY_CHANGE_ACTION:
ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG));
break;
case android.hardware.Camera.ACTION_NEW_PICTURE:
case android.hardware.Camera.ACTION_NEW_VIDEO:

View File

@@ -309,22 +309,4 @@ public class ProxyTracker {
}
}
}
/**
* Enable or disable the default proxy.
*
* This sets the flag for enabling/disabling the default proxy and sends the broadcast
* if applicable.
* @param enabled whether the default proxy should be enabled.
*/
public void setDefaultProxyEnabled(final boolean enabled) {
synchronized (mProxyLock) {
if (mDefaultProxyEnabled != enabled) {
mDefaultProxyEnabled = enabled;
if (mGlobalProxy == null && mDefaultProxy != null) {
sendProxyBroadcast();
}
}
}
}
}

View File

@@ -831,6 +831,8 @@ public class Vpn {
}
}
lp.setHttpProxy(mConfig.proxyInfo);
if (!allowIPv4) {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
}

View File

@@ -123,6 +123,7 @@ import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.SocketKeepalive;
import android.net.UidRange;
@@ -161,6 +162,7 @@ import com.android.server.connectivity.DefaultNetworkMetrics;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
@@ -1006,6 +1008,11 @@ public class ConnectivityServiceTest {
return mock(Tethering.class);
}
@Override
protected ProxyTracker makeProxyTracker() {
return mock(ProxyTracker.class);
}
@Override
protected int reserveNetId() {
while (true) {
@@ -1028,6 +1035,11 @@ public class ConnectivityServiceTest {
}
}
@Override
protected boolean queryUserAccess(int uid, int netId) {
return true;
}
public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
}
@@ -5132,4 +5144,84 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.sendLinkProperties(lp);
verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES);
}
@Test
public void testGetGlobalProxyForNetwork() {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
}
@Test
public void testGetProxyForActiveNetwork() {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
final LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(testProxyInfo);
mWiFiNetworkAgent.sendLinkProperties(testLinkProperties);
waitForIdle();
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
}
@Test
public void testGetProxyForVPN() {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
// Set up a WiFi network with no proxy
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
// Set up a VPN network with a proxy
final int uid = Process.myUid();
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(testProxyInfo);
vpnNetworkAgent.sendLinkProperties(testLinkProperties);
waitForIdle();
// Connect to VPN with proxy
mMockVpn.setNetworkAgent(vpnNetworkAgent);
vpnNetworkAgent.connect(true);
mMockVpn.connect();
waitForIdle();
// Test that the VPN network returns a proxy, and the WiFi does not.
assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
// Test that the VPN network returns no proxy when it is set to null.
testLinkProperties.setHttpProxy(null);
vpnNetworkAgent.sendLinkProperties(testLinkProperties);
waitForIdle();
assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
assertNull(mService.getProxyForNetwork(null));
// Set WiFi proxy and check that the vpn proxy is still null.
testLinkProperties.setHttpProxy(testProxyInfo);
mWiFiNetworkAgent.sendLinkProperties(testLinkProperties);
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
// Disconnect from VPN and check that the active network, which is now the WiFi, has the
// correct proxy setting.
vpnNetworkAgent.disconnect();
waitForIdle();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
}
}