Merge "Nat464Xlat: interface notification handler on ConnectivityService"
am: 91817b3129 Change-Id: I2f002cd197e2eeaaadadd747a6b33d264cd34433
This commit is contained in:
@@ -2023,16 +2023,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
|
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
|
||||||
if (VDBG) {
|
handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
|
||||||
log("Update of LinkProperties for " + nai.name() +
|
|
||||||
"; created=" + nai.created +
|
|
||||||
"; everConnected=" + nai.everConnected);
|
|
||||||
}
|
|
||||||
LinkProperties oldLp = nai.linkProperties;
|
|
||||||
synchronized (nai) {
|
|
||||||
nai.linkProperties = (LinkProperties)msg.obj;
|
|
||||||
}
|
|
||||||
if (nai.everConnected) updateLinkProperties(nai, oldLp);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
|
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
|
||||||
@@ -2281,7 +2272,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
|
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
|
||||||
mNetworkAgentInfos.remove(msg.replyTo);
|
mNetworkAgentInfos.remove(msg.replyTo);
|
||||||
maybeStopClat(nai);
|
nai.maybeStopClat();
|
||||||
synchronized (mNetworkForNetId) {
|
synchronized (mNetworkForNetId) {
|
||||||
// Remove the NetworkAgent, but don't mark the netId as
|
// Remove the NetworkAgent, but don't mark the netId as
|
||||||
// available until we've told netd to delete it below.
|
// available until we've told netd to delete it below.
|
||||||
@@ -4375,7 +4366,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
updateDnses(newLp, oldLp, netId);
|
updateDnses(newLp, oldLp, netId);
|
||||||
|
|
||||||
// Start or stop clat accordingly to network state.
|
// Start or stop clat accordingly to network state.
|
||||||
updateClat(networkAgent);
|
networkAgent.updateClat(mNetd);
|
||||||
if (isDefaultNetwork(networkAgent)) {
|
if (isDefaultNetwork(networkAgent)) {
|
||||||
handleApplyDefaultProxy(newLp.getHttpProxy());
|
handleApplyDefaultProxy(newLp.getHttpProxy());
|
||||||
} else {
|
} else {
|
||||||
@@ -4390,32 +4381,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
|
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateClat(NetworkAgentInfo nai) {
|
|
||||||
if (Nat464Xlat.requiresClat(nai)) {
|
|
||||||
maybeStartClat(nai);
|
|
||||||
} else {
|
|
||||||
maybeStopClat(nai);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Ensure clat has started for this network. */
|
|
||||||
private void maybeStartClat(NetworkAgentInfo nai) {
|
|
||||||
if (nai.clatd != null && nai.clatd.isStarted()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nai.clatd = new Nat464Xlat(mNetd, mTrackerHandler, nai);
|
|
||||||
nai.clatd.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Ensure clat has stopped for this network. */
|
|
||||||
private void maybeStopClat(NetworkAgentInfo nai) {
|
|
||||||
if (nai.clatd == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nai.clatd.stop();
|
|
||||||
nai.clatd = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
|
private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
|
||||||
// Marks are only available on WiFi interaces. Checking for
|
// Marks are only available on WiFi interaces. Checking for
|
||||||
// marks on unsupported interfaces is harmless.
|
// marks on unsupported interfaces is harmless.
|
||||||
@@ -4650,6 +4615,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
|
||||||
|
if (VDBG) {
|
||||||
|
log("Update of LinkProperties for " + nai.name() +
|
||||||
|
"; created=" + nai.created +
|
||||||
|
"; everConnected=" + nai.everConnected);
|
||||||
|
}
|
||||||
|
LinkProperties oldLp = nai.linkProperties;
|
||||||
|
synchronized (nai) {
|
||||||
|
nai.linkProperties = newLp;
|
||||||
|
}
|
||||||
|
if (nai.everConnected) {
|
||||||
|
updateLinkProperties(nai, oldLp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
|
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
|
||||||
for (int i = 0; i < nai.numNetworkRequests(); i++) {
|
for (int i = 0; i < nai.numNetworkRequests(); i++) {
|
||||||
NetworkRequest nr = nai.requestAt(i);
|
NetworkRequest nr = nai.requestAt(i);
|
||||||
|
|||||||
@@ -20,22 +20,21 @@ import android.net.InterfaceConfiguration;
|
|||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.NetworkAgent;
|
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.INetworkManagementService;
|
import android.os.INetworkManagementService;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.server.net.BaseNetworkObserver;
|
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
|
import com.android.server.net.BaseNetworkObserver;
|
||||||
|
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to manage a 464xlat CLAT daemon.
|
* Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
|
||||||
|
* from a consistent and unique thread context. It is the responsability of ConnectivityService to
|
||||||
|
* call into this class from its own Handler thread.
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -55,9 +54,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
|
|
||||||
private final INetworkManagementService mNMService;
|
private final INetworkManagementService mNMService;
|
||||||
|
|
||||||
// ConnectivityService Handler for LinkProperties updates.
|
|
||||||
private final Handler mHandler;
|
|
||||||
|
|
||||||
// The network we're running on, and its type.
|
// The network we're running on, and its type.
|
||||||
private final NetworkAgentInfo mNetwork;
|
private final NetworkAgentInfo mNetwork;
|
||||||
|
|
||||||
@@ -67,16 +63,12 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
RUNNING; // start() called, and the stacked iface is known to be up.
|
RUNNING; // start() called, and the stacked iface is known to be up.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
|
|
||||||
// its handler thread must not modify any internal state variables; they are only updated by the
|
|
||||||
// interface observers, called on the notification threads.
|
|
||||||
private String mBaseIface;
|
private String mBaseIface;
|
||||||
private String mIface;
|
private String mIface;
|
||||||
private volatile State mState = State.IDLE;
|
private State mState = State.IDLE;
|
||||||
|
|
||||||
public Nat464Xlat(INetworkManagementService nmService, Handler handler, NetworkAgentInfo nai) {
|
public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
|
||||||
mNMService = nmService;
|
mNMService = nmService;
|
||||||
mHandler = handler;
|
|
||||||
mNetwork = nai;
|
mNetwork = nai;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +96,13 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
return mState != State.IDLE;
|
return mState != State.IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if clatd has been started but the stacked interface is not yet up.
|
||||||
|
*/
|
||||||
|
public boolean isStarting() {
|
||||||
|
return mState == State.STARTING;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if clatd has been started and the stacked interface is up.
|
* @return true if clatd has been started and the stacked interface is up.
|
||||||
*/
|
*/
|
||||||
@@ -121,7 +120,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears internal state. Must not be called by ConnectivityService.
|
* Clears internal state.
|
||||||
*/
|
*/
|
||||||
private void enterIdleState() {
|
private void enterIdleState() {
|
||||||
mIface = null;
|
mIface = null;
|
||||||
@@ -130,7 +129,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the clat daemon. Called by ConnectivityService on the handler thread.
|
* Starts the clat daemon.
|
||||||
*/
|
*/
|
||||||
public void start() {
|
public void start() {
|
||||||
if (isStarted()) {
|
if (isStarted()) {
|
||||||
@@ -167,7 +166,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the clat daemon. Called by ConnectivityService on the handler thread.
|
* Stops the clat daemon.
|
||||||
*/
|
*/
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (!isStarted()) {
|
if (!isStarted()) {
|
||||||
@@ -181,15 +180,8 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
} catch(RemoteException|IllegalStateException e) {
|
} catch(RemoteException|IllegalStateException e) {
|
||||||
Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
|
Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
|
||||||
}
|
}
|
||||||
// When clatd stops and its interface is deleted, interfaceRemoved() will notify
|
// When clatd stops and its interface is deleted, handleInterfaceRemoved() will trigger
|
||||||
// ConnectivityService and call enterIdleState().
|
// ConnectivityService#handleUpdateLinkProperties and call enterIdleState().
|
||||||
}
|
|
||||||
|
|
||||||
private void updateConnectivityService(LinkProperties lp) {
|
|
||||||
Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
|
|
||||||
msg.replyTo = mNetwork.messenger;
|
|
||||||
Slog.i(TAG, "sending message to ConnectivityService: " + msg);
|
|
||||||
msg.sendToTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,19 +249,15 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds stacked link on base link and transitions to Running state
|
* Adds stacked link on base link and transitions to RUNNING state.
|
||||||
* This is called by the InterfaceObserver on its own thread, so can race with stop().
|
|
||||||
*/
|
*/
|
||||||
@Override
|
private void handleInterfaceLinkStateChanged(String iface, boolean up) {
|
||||||
public void interfaceLinkStateChanged(String iface, boolean up) {
|
if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
|
||||||
if (!isStarted() || !up || !Objects.equals(mIface, iface)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isRunning()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LinkAddress clatAddress = getLinkAddress(iface);
|
LinkAddress clatAddress = getLinkAddress(iface);
|
||||||
if (clatAddress == null) {
|
if (clatAddress == null) {
|
||||||
|
Slog.e(TAG, "cladAddress was null for stacked iface " + iface);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mState = State.RUNNING;
|
mState = State.RUNNING;
|
||||||
@@ -279,15 +267,14 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
maybeSetIpv6NdOffload(mBaseIface, false);
|
maybeSetIpv6NdOffload(mBaseIface, false);
|
||||||
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
|
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
|
||||||
lp.addStackedLink(makeLinkProperties(clatAddress));
|
lp.addStackedLink(makeLinkProperties(clatAddress));
|
||||||
updateConnectivityService(lp);
|
mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void interfaceRemoved(String iface) {
|
* Removes stacked link on base link and transitions to IDLE state.
|
||||||
if (!isStarted() || !Objects.equals(mIface, iface)) {
|
*/
|
||||||
return;
|
private void handleInterfaceRemoved(String iface) {
|
||||||
}
|
if (!isRunning() || !Objects.equals(mIface, iface)) {
|
||||||
if (!isRunning()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,21 +282,28 @@ public class Nat464Xlat extends BaseNetworkObserver {
|
|||||||
// The interface going away likely means clatd has crashed. Ask netd to stop it,
|
// The interface going away likely means clatd has crashed. Ask netd to stop it,
|
||||||
// because otherwise when we try to start it again on the same base interface netd
|
// because otherwise when we try to start it again on the same base interface netd
|
||||||
// will complain that it's already started.
|
// will complain that it's already started.
|
||||||
//
|
|
||||||
// Note that this method can be called by the interface observer at the same time
|
|
||||||
// that ConnectivityService calls stop(). In this case, the second call to
|
|
||||||
// stopClatd() will just throw IllegalStateException, which we'll ignore.
|
|
||||||
try {
|
try {
|
||||||
mNMService.unregisterObserver(this);
|
mNMService.unregisterObserver(this);
|
||||||
|
// TODO: add STOPPING state to avoid calling stopClatd twice.
|
||||||
mNMService.stopClatd(mBaseIface);
|
mNMService.stopClatd(mBaseIface);
|
||||||
} catch(RemoteException|IllegalStateException e) {
|
} catch(RemoteException|IllegalStateException e) {
|
||||||
// Well, we tried.
|
Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
|
||||||
}
|
}
|
||||||
maybeSetIpv6NdOffload(mBaseIface, true);
|
maybeSetIpv6NdOffload(mBaseIface, true);
|
||||||
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
|
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
|
||||||
lp.removeStackedLink(mIface);
|
lp.removeStackedLink(mIface);
|
||||||
enterIdleState();
|
enterIdleState();
|
||||||
updateConnectivityService(lp);
|
mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interfaceLinkStateChanged(String iface, boolean up) {
|
||||||
|
mNetwork.handler.post(() -> { handleInterfaceLinkStateChanged(iface, up); });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interfaceRemoved(String iface) {
|
||||||
|
mNetwork.handler.post(() -> { handleInterfaceRemoved(iface); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ import android.net.NetworkMisc;
|
|||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.NetworkState;
|
import android.net.NetworkState;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.INetworkManagementService;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@@ -247,9 +249,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
|
|
||||||
private static final String TAG = ConnectivityService.class.getSimpleName();
|
private static final String TAG = ConnectivityService.class.getSimpleName();
|
||||||
private static final boolean VDBG = false;
|
private static final boolean VDBG = false;
|
||||||
private final ConnectivityService mConnService;
|
public final ConnectivityService connService;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final Handler mHandler;
|
final Handler handler;
|
||||||
|
|
||||||
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
|
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
|
||||||
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
|
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
|
||||||
@@ -261,10 +263,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
linkProperties = lp;
|
linkProperties = lp;
|
||||||
networkCapabilities = nc;
|
networkCapabilities = nc;
|
||||||
currentScore = score;
|
currentScore = score;
|
||||||
mConnService = connService;
|
this.connService = connService;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mHandler = handler;
|
this.handler = handler;
|
||||||
networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
|
networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
|
||||||
networkMisc = misc;
|
networkMisc = misc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +432,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
private boolean ignoreWifiUnvalidationPenalty() {
|
private boolean ignoreWifiUnvalidationPenalty() {
|
||||||
boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
|
boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
|
||||||
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
||||||
boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
|
boolean avoidBadWifi = connService.avoidBadWifi() || avoidUnvalidated;
|
||||||
return isWifi && !avoidBadWifi && everValidated;
|
return isWifi && !avoidBadWifi && everValidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,8 +516,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newExpiry > 0) {
|
if (newExpiry > 0) {
|
||||||
mLingerMessage = mConnService.makeWakeupMessage(
|
mLingerMessage = connService.makeWakeupMessage(
|
||||||
mContext, mHandler,
|
mContext, handler,
|
||||||
"NETWORK_LINGER_COMPLETE." + network.netId,
|
"NETWORK_LINGER_COMPLETE." + network.netId,
|
||||||
EVENT_NETWORK_LINGER_COMPLETE, this);
|
EVENT_NETWORK_LINGER_COMPLETE, this);
|
||||||
mLingerMessage.schedule(newExpiry);
|
mLingerMessage.schedule(newExpiry);
|
||||||
@@ -551,6 +553,32 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
|
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateClat(INetworkManagementService netd) {
|
||||||
|
if (Nat464Xlat.requiresClat(this)) {
|
||||||
|
maybeStartClat(netd);
|
||||||
|
} else {
|
||||||
|
maybeStopClat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ensure clat has started for this network. */
|
||||||
|
public void maybeStartClat(INetworkManagementService netd) {
|
||||||
|
if (clatd != null && clatd.isStarted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clatd = new Nat464Xlat(netd, this);
|
||||||
|
clatd.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ensure clat has stopped for this network. */
|
||||||
|
public void maybeStopClat() {
|
||||||
|
if (clatd == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clatd.stop();
|
||||||
|
clatd = null;
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
|
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
|
||||||
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
|
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
|
||||||
|
|||||||
Reference in New Issue
Block a user