Merge tag 'android-security-10.0.0_r56' into int/10/fp2

Android security 10.0.0 release 56

* tag 'android-security-10.0.0_r56':
  Fix the security issue that preloaded apps can get SSID & BSSID

Change-Id: Ia1e877871ebe842d908633cd4baed47c12b07082
diff --git a/Android.bp b/Android.bp
index c013b8c..1e583e1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,6 +49,7 @@
         "captiveportal-lib",
     ],
     manifest: "AndroidManifestBase.xml",
+    plugins: ["java_api_finder"],
 }
 
 cc_library_shared {
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 250feee..5dcf27e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,7 +19,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.android.networkstack"
   android:sharedUserId="android.uid.networkstack"
-  android:versionCode="290000000"
+  android:versionCode="299900000"
   android:versionName="2019-09"
 >
 
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index ca6c17a..c45fc82 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -28,6 +28,7 @@
 import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
 import static android.net.util.NetworkStackUtils.closeSocketQuietly;
 import static android.net.util.SocketUtils.makePacketSocketAddress;
 import static android.system.OsConstants.AF_INET;
@@ -43,11 +44,16 @@
 
 import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.DhcpResults;
 import android.net.InetAddresses;
+import android.net.NetworkStackIpMemoryStore;
 import android.net.TrafficStats;
 import android.net.ip.IpClient;
+import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.OnStatusListener;
 import android.net.metrics.DhcpClientEvent;
 import android.net.metrics.DhcpErrorEvent;
 import android.net.metrics.IpConnectivityLog;
@@ -62,6 +68,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.State;
@@ -118,8 +125,9 @@
 
     // Timers and timeouts.
     private static final int SECONDS = 1000;
-    private static final int FIRST_TIMEOUT_MS   =   2 * SECONDS;
-    private static final int MAX_TIMEOUT_MS     = 128 * SECONDS;
+    private static final int FIRST_TIMEOUT_MS         =   2 * SECONDS;
+    private static final int MAX_TIMEOUT_MS           = 128 * SECONDS;
+    private static final int IPMEMORYSTORE_TIMEOUT_MS =   1 * SECONDS;
 
     // This is not strictly needed, since the client is asynchronous and implements exponential
     // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
@@ -166,6 +174,12 @@
     private static final int CMD_RENEW_DHCP       = PRIVATE_BASE + 4;
     private static final int CMD_REBIND_DHCP      = PRIVATE_BASE + 5;
     private static final int CMD_EXPIRE_DHCP      = PRIVATE_BASE + 6;
+    private static final int EVENT_CONFIGURATION_TIMEOUT   = PRIVATE_BASE + 7;
+    private static final int EVENT_CONFIGURATION_OBTAINED  = PRIVATE_BASE + 8;
+    private static final int EVENT_CONFIGURATION_INVALID   = PRIVATE_BASE + 9;
+
+    // constant to represent this DHCP lease has been expired.
+    private static final long EXPIRED_LEASE = 1L;
 
     // For message logging.
     private static final Class[] sMessageClasses = { DhcpClient.class };
@@ -222,6 +236,12 @@
     private DhcpResults mDhcpLease;
     private long mDhcpLeaseExpiry;
     private DhcpResults mOffer;
+    private String mL2Key;
+    private Inet4Address mLastAssignedIpv4Address;
+    private long mLastAssignedIpv4AddressExpiry;
+    private boolean mDhcpLeaseCacheEnabled;
+    @NonNull
+    private final NetworkStackIpMemoryStore mIpMemoryStore;
 
     // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
     private long mLastInitEnterTime;
@@ -240,8 +260,11 @@
     private State mDhcpRebindingState = new DhcpRebindingState();
     private State mDhcpInitRebootState = new DhcpInitRebootState();
     private State mDhcpRebootingState = new DhcpRebootingState();
+    private State mObtainingConfigurationState = new ObtainingConfigurationState();
     private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
     private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
+    private State mWaitBeforeObtainingConfigurationState =
+            new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState);
 
     private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
         cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
@@ -249,17 +272,22 @@
     }
 
     // TODO: Take an InterfaceParams instance instead of an interface name String.
-    private DhcpClient(Context context, StateMachine controller, String iface) {
+    private DhcpClient(Context context, StateMachine controller, String iface,
+            NetworkStackIpMemoryStore ipMemoryStore) {
         super(TAG, controller.getHandler());
 
         mContext = context;
         mController = controller;
         mIfaceName = iface;
+        mIpMemoryStore = ipMemoryStore;
 
+        // CHECKSTYLE:OFF IndentationCheck
         addState(mStoppedState);
         addState(mDhcpState);
             addState(mDhcpInitState, mDhcpState);
             addState(mWaitBeforeStartState, mDhcpState);
+            addState(mWaitBeforeObtainingConfigurationState, mDhcpState);
+            addState(mObtainingConfigurationState, mDhcpState);
             addState(mDhcpSelectingState, mDhcpState);
             addState(mDhcpRequestingState, mDhcpState);
             addState(mDhcpHaveLeaseState, mDhcpState);
@@ -270,6 +298,7 @@
                 addState(mDhcpRebindingState, mDhcpHaveLeaseState);
             addState(mDhcpInitRebootState, mDhcpState);
             addState(mDhcpRebootingState, mDhcpState);
+        // CHECKSTYLE:ON IndentationCheck
 
         setInitialState(mStoppedState);
 
@@ -290,13 +319,34 @@
     }
 
     public static DhcpClient makeDhcpClient(
-            Context context, StateMachine controller, InterfaceParams ifParams) {
-        DhcpClient client = new DhcpClient(context, controller, ifParams.name);
+            Context context, StateMachine controller, InterfaceParams ifParams,
+            NetworkStackIpMemoryStore ipMemoryStore) {
+        DhcpClient client = new DhcpClient(context, controller, ifParams.name, ipMemoryStore);
         client.mIface = ifParams;
         client.start();
         return client;
     }
 
+    /**
+     * check whether or not to support caching the last lease info and INIT-REBOOT state.
+     *
+     */
+    public boolean isDhcpLeaseCacheEnabled() {
+        mDhcpLeaseCacheEnabled = Boolean.parseBoolean(NetworkStackUtils.getDeviceConfigProperty(
+                NetworkStackUtils.NAMESPACE_CONNECTIVITY,
+                NetworkStackUtils.DHCP_INIT_REBOOT_ENABLED, "false"));
+        return mDhcpLeaseCacheEnabled;
+    }
+
+    /**
+     * set DHCP lease cached enabled experiment flag.
+     *
+     */
+    @VisibleForTesting
+    public void setDhcpLeaseCacheEnabled(final boolean enabled) {
+        mDhcpLeaseCacheEnabled = enabled;
+    }
+
     private boolean initInterface() {
         if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
         if (mIface == null) {
@@ -491,12 +541,48 @@
         Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
     }
 
+    private void setLeaseExpiredToIpMemoryStore() {
+        final String l2Key = mL2Key;
+        if (l2Key == null) return;
+        final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+        // TODO: clear out the address and lease instead of storing an expired lease
+        na.setAssignedV4AddressExpiry(EXPIRED_LEASE);
+
+        final OnStatusListener listener = status -> {
+            if (!status.isSuccess()) Log.e(TAG, "Failed to set lease expiry, status: " + status);
+        };
+        mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
+    }
+
+    private void maybeSaveLeaseToIpMemoryStore() {
+        final String l2Key = mL2Key;
+        if (l2Key == null || mDhcpLease == null || mDhcpLease.ipAddress == null) return;
+        final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+        na.setAssignedV4Address((Inet4Address) mDhcpLease.ipAddress.getAddress());
+        na.setAssignedV4AddressExpiry((mDhcpLease.leaseDuration == INFINITE_LEASE)
+                ? Long.MAX_VALUE
+                : mDhcpLease.leaseDuration * 1000 + System.currentTimeMillis());
+        na.setDnsAddresses(mDhcpLease.dnsServers);
+        na.setMtu(mDhcpLease.mtu);
+
+        final OnStatusListener listener = status -> {
+            if (!status.isSuccess()) Log.e(TAG, "Failed to store network attrs, status: " + status);
+        };
+        mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
+    }
+
     private void notifySuccess() {
+        if (isDhcpLeaseCacheEnabled()) {
+            maybeSaveLeaseToIpMemoryStore();
+        }
         mController.sendMessage(
                 CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
     }
 
     private void notifyFailure() {
+        if (isDhcpLeaseCacheEnabled()) {
+            setLeaseExpiredToIpMemoryStore();
+        }
         mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
     }
 
@@ -616,10 +702,13 @@
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case CMD_START_DHCP:
+                    mL2Key = (String) message.obj;
                     if (mRegisteredForPreDhcpNotification) {
-                        transitionTo(mWaitBeforeStartState);
+                        transitionTo(isDhcpLeaseCacheEnabled()
+                                ? mWaitBeforeObtainingConfigurationState : mWaitBeforeStartState);
                     } else {
-                        transitionTo(mDhcpInitState);
+                        transitionTo(isDhcpLeaseCacheEnabled()
+                                ? mObtainingConfigurationState : mDhcpInitState);
                     }
                     return HANDLED;
                 default:
@@ -629,14 +718,21 @@
     }
 
     class WaitBeforeStartState extends WaitBeforeOtherState {
-        public WaitBeforeStartState(State otherState) {
+        WaitBeforeStartState(State otherState) {
             super();
             mOtherState = otherState;
         }
     }
 
     class WaitBeforeRenewalState extends WaitBeforeOtherState {
-        public WaitBeforeRenewalState(State otherState) {
+        WaitBeforeRenewalState(State otherState) {
+            super();
+            mOtherState = otherState;
+        }
+    }
+
+    class WaitBeforeObtainingConfigurationState extends WaitBeforeOtherState {
+        WaitBeforeObtainingConfigurationState(State otherState) {
             super();
             mOtherState = otherState;
         }
@@ -782,6 +878,69 @@
         }
     }
 
+    class ObtainingConfigurationState extends LoggingState {
+        @Override
+        public void enter() {
+            super.enter();
+
+            // Set a timeout for retrieving network attributes operation
+            sendMessageDelayed(EVENT_CONFIGURATION_TIMEOUT, IPMEMORYSTORE_TIMEOUT_MS);
+
+            final OnNetworkAttributesRetrievedListener listener = (status, l2Key, attributes) -> {
+                if (null == attributes || null == attributes.assignedV4Address) {
+                    if (!status.isSuccess()) {
+                        Log.e(TAG, "Error retrieving network attributes: " + status);
+                    }
+                    sendMessage(EVENT_CONFIGURATION_INVALID);
+                    return;
+                }
+                sendMessage(EVENT_CONFIGURATION_OBTAINED, attributes);
+            };
+            mIpMemoryStore.retrieveNetworkAttributes(mL2Key, listener);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            super.processMessage(message);
+            switch (message.what) {
+                case EVENT_CONFIGURATION_INVALID:
+                case EVENT_CONFIGURATION_TIMEOUT:
+                    transitionTo(mDhcpInitState);
+                    return HANDLED;
+
+                case EVENT_CONFIGURATION_OBTAINED:
+                    final long currentTime = System.currentTimeMillis();
+                    NetworkAttributes attributes = (NetworkAttributes) message.obj;
+                    if (DBG) {
+                        Log.d(TAG, "l2key: " + mL2Key
+                                + " lease address: " + attributes.assignedV4Address
+                                + " lease expiry: "  + attributes.assignedV4AddressExpiry
+                                + " current time: "  + currentTime);
+                    }
+                    if (currentTime >= attributes.assignedV4AddressExpiry) {
+                        // Lease has expired.
+                        transitionTo(mDhcpInitState);
+                        return HANDLED;
+                    }
+                    mLastAssignedIpv4Address = attributes.assignedV4Address;
+                    mLastAssignedIpv4AddressExpiry = attributes.assignedV4AddressExpiry;
+                    transitionTo(mDhcpInitRebootState);
+                    return HANDLED;
+
+                default:
+                    deferMessage(message);
+                    return HANDLED;
+            }
+        }
+
+        @Override
+        public void exit() {
+            removeMessages(EVENT_CONFIGURATION_INVALID);
+            removeMessages(EVENT_CONFIGURATION_TIMEOUT);
+            removeMessages(EVENT_CONFIGURATION_OBTAINED);
+        }
+    }
+
     class DhcpInitState extends PacketRetransmittingState {
         public DhcpInitState() {
             super();
@@ -1050,7 +1209,35 @@
         }
     }
 
-    class DhcpInitRebootState extends LoggingState {
+    class DhcpInitRebootState extends DhcpRequestingState {
+        @Override
+        public void enter() {
+            super.enter();
+            startNewTransaction();
+        }
+
+        // RFC 2131 4.3.2 describes generated DHCPREQUEST message during
+        // INIT-REBOOT state:
+        // 'server identifier' MUST NOT be filled in, 'requested IP address'
+        // option MUST be filled in with client's notion of its previously
+        // assigned address. 'ciaddr' MUST be zero. The client is seeking to
+        // verify a previously allocated, cached configuration. Server SHOULD
+        // send a DHCPNAK message to the client if the 'requested IP address'
+        // is incorrect, or is on the wrong network.
+        @Override
+        protected boolean sendPacket() {
+            return sendRequestPacket(
+                     INADDR_ANY,                                       // ciaddr
+                     mLastAssignedIpv4Address,                         // DHCP_REQUESTED_IP
+                     null,                                             // DHCP_SERVER_IDENTIFIER
+                     INADDR_BROADCAST);                                // packet destination address
+        }
+
+        @Override
+        public void exit() {
+            mLastAssignedIpv4Address = null;
+            mLastAssignedIpv4AddressExpiry = 0;
+        }
     }
 
     class DhcpRebootingState extends LoggingState {
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 266b1b0..e1b5e9f 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -78,7 +78,6 @@
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-
 /**
  * IpClient
  *
@@ -394,6 +393,14 @@
         public INetd getNetd(Context context) {
             return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
         }
+
+        /**
+         * Get a IpMemoryStore instance.
+         */
+        public NetworkStackIpMemoryStore getIpMemoryStore(Context context,
+                NetworkStackServiceManager nssManager) {
+            return new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
+        }
     }
 
     public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -418,8 +425,7 @@
         mShutdownLatch = new CountDownLatch(1);
         mCm = mContext.getSystemService(ConnectivityManager.class);
         mObserverRegistry = observerRegistry;
-        mIpMemoryStore =
-                new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
+        mIpMemoryStore = deps.getIpMemoryStore(context, nssManager);
 
         sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
         mLog = sSmLogs.get(mInterfaceName);
@@ -934,7 +940,7 @@
         // accompanying code in IpReachabilityMonitor) is unreachable.
         final boolean ignoreIPv6ProvisioningLoss =
                 mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
-                && mCm.shouldAvoidBadWifi();
+                && !mCm.shouldAvoidBadWifi();
 
         // Additionally:
         //
@@ -1124,6 +1130,7 @@
         }
         mCallback.onNewDhcpResults(dhcpResults);
         maybeSaveNetworkToIpMemoryStore();
+
         dispatchCallback(delta, newLp);
     }
 
@@ -1182,9 +1189,10 @@
             }
         } else {
             // Start DHCPv4.
-            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
+            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
+                    mIpMemoryStore);
             mDhcpClient.registerForPreDhcpNotification();
-            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
+            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, mL2Key);
         }
 
         return true;
@@ -1369,7 +1377,6 @@
         @Override
         public void enter() {
             mStartTimeMillis = SystemClock.elapsedRealtime();
-
             if (mConfiguration.mProvisioningTimeoutMs > 0) {
                 final long alarmTime = SystemClock.elapsedRealtime()
                         + mConfiguration.mProvisioningTimeoutMs;
@@ -1426,7 +1433,6 @@
                 case EVENT_PROVISIONING_TIMEOUT:
                     handleProvisioningFailure();
                     break;
-
                 default:
                     // It's safe to process messages out of order because the
                     // only message that can both
diff --git a/src/android/net/ip/IpNeighborMonitor.java b/src/android/net/ip/IpNeighborMonitor.java
index 6ae9a2b..803f2e6 100644
--- a/src/android/net/ip/IpNeighborMonitor.java
+++ b/src/android/net/ip/IpNeighborMonitor.java
@@ -185,12 +185,6 @@
                 break;
             }
 
-            final int srcPortId = nlMsg.getHeader().nlmsg_pid;
-            if (srcPortId !=  0) {
-                mLog.e("non-kernel source portId: " + Integer.toUnsignedLong(srcPortId));
-                break;
-            }
-
             if (nlMsg instanceof NetlinkErrorMessage) {
                 mLog.e("netlink error: " + nlMsg);
                 continue;
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java
index 9bf1b96..978cedb 100644
--- a/src/android/net/util/NetworkStackUtils.java
+++ b/src/android/net/util/NetworkStackUtils.java
@@ -108,6 +108,11 @@
      */
     public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
 
+    /**
+     * Experiment flag to enable DHCP INIT-REBOOT state, default value is false.
+     */
+    public static final String DHCP_INIT_REBOOT_ENABLED = "dhcp_init_reboot_enabled";
+
     static {
         System.loadLibrary("networkstackutilsjni");
     }
diff --git a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
index a538a5b..4896ef2 100644
--- a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
@@ -73,7 +73,8 @@
         public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER";
 
         public static final String COLNAME_ASSIGNEDV4ADDRESSEXPIRY = "assignedV4AddressExpiry";
-        // The lease expiry timestamp in uint of milliseconds
+        // The lease expiry timestamp in uint of milliseconds since the Epoch. Long.MAX_VALUE
+        // is used to represent "infinite lease".
         public static final String COLTYPE_ASSIGNEDV4ADDRESSEXPIRY = "BIGINT";
 
         // Please note that the group hint is only a *hint*, hence its name. The client can offer
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java
index 5f80006..1d6ce6e 100644
--- a/tests/unit/src/android/net/ip/IpClientTest.java
+++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -55,6 +55,7 @@
 import com.android.server.NetworkObserver;
 import com.android.server.NetworkObserverRegistry;
 import com.android.server.NetworkStackService;
+import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,6 +70,7 @@
 import java.util.List;
 import java.util.Set;
 
+
 /**
  * Tests for IpClient.
  */
@@ -98,6 +100,8 @@
     @Mock private ContentResolver mContentResolver;
     @Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager;
     @Mock private NetworkStackIpMemoryStore mIpMemoryStore;
+    @Mock private IpMemoryStoreService mIpMemoryStoreService;
+    @Mock private InterfaceParams mInterfaceParams;
 
     private NetworkObserver mObserver;
     private InterfaceParams mIfParams;
@@ -113,6 +117,11 @@
         when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
                 .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mNetworkStackServiceManager.getIpMemoryStoreService())
+                .thenReturn(mIpMemoryStoreService);
+        when(mDependencies.getInterfaceParams(any())).thenReturn(mInterfaceParams);
+        when(mDependencies.getIpMemoryStore(mContext, mNetworkStackServiceManager))
+                .thenReturn(mIpMemoryStore);
 
         mIfParams = null;
     }
diff --git a/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
index 64fe3a6..ba86fcf 100644
--- a/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -224,7 +224,7 @@
         };
     }
 
-    /** Helper method to make an IOnSameNetworkResponseListener */
+    /** Helper method to make an IOnSameL3NetworkResponseListener */
     private interface OnSameL3NetworkResponseListener {
         void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer);
     }