Merge changes Iaafb2f9a,I90777d4c

* changes:
  Retry IKE INIT if receiving Notify-Cookie
  Support Cookie notification type
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index c970268..c3c648d 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -32,6 +32,7 @@
 import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_PARTIAL;
 import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_PROTECTED_ERROR;
 import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_UNPROTECTED_ERROR;
+import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_COOKIE;
 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_COOKIE2;
 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION;
 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED;
@@ -2072,7 +2073,7 @@
                             IkeNotifyPayload notify = (IkeNotifyPayload) payload;
                             if (notify.notifyType == NOTIFY_TYPE_COOKIE2) {
                                 infoPayloadList.add(
-                                        IkeNotifyPayload.handleCookie2AndGenerateResponse(notify));
+                                        IkeNotifyPayload.handleCookie2AndGenerateCopy(notify));
                             }
 
                             // No action for other notifications
@@ -2930,23 +2931,27 @@
         @Override
         public void enterState() {
             try {
-                IkeMessage request = buildIkeInitReq();
-
-                // Register local SPI to receive the IKE INIT response.
-                mIkeSocket.registerIke(
-                        request.ikeHeader.ikeInitiatorSpi, IkeSessionStateMachine.this);
-
-                mIkeInitRequestBytes = request.encode();
-                mIkeInitNoncePayload =
-                        request.getPayloadForType(
-                                IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class);
-                mRetransmitter = new UnencryptedRetransmitter(request);
+                sendRequest(buildIkeInitReq());
             } catch (IOException e) {
                 // Fail to assign IKE SPI
                 handleIkeFatalError(e);
             }
         }
 
+        private void sendRequest(IkeMessage request) {
+            // Register local SPI to receive the IKE INIT response.
+            mIkeSocket.registerIke(request.ikeHeader.ikeInitiatorSpi, IkeSessionStateMachine.this);
+
+            mIkeInitRequestBytes = request.encode();
+            mIkeInitNoncePayload =
+                    request.getPayloadForType(IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class);
+
+            if (mRetransmitter != null) {
+                mRetransmitter.stopRetransmitting();
+            }
+            mRetransmitter = new UnencryptedRetransmitter(request);
+        }
+
         @Override
         protected void triggerRetransmit() {
             mRetransmitter.retransmit();
@@ -3023,10 +3028,41 @@
             }
         }
 
+        /** Returns the Notify-Cookie payload, or null if it does not exist */
+        private IkeNotifyPayload getNotifyCookie(IkeMessage ikeMessage) {
+            List<IkeNotifyPayload> notifyPayloads =
+                    ikeMessage.getPayloadListForType(PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class);
+            for (IkeNotifyPayload notify : notifyPayloads) {
+                if (notify.notifyType == NOTIFY_TYPE_COOKIE) {
+                    return notify;
+                }
+            }
+            return null;
+        }
+
         @Override
         protected void handleResponseIkeMessage(IkeMessage ikeMessage) {
             boolean ikeInitSuccess = false;
             try {
+                int exchangeType = ikeMessage.ikeHeader.exchangeType;
+                if (exchangeType != IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT) {
+                    throw new InvalidSyntaxException(
+                            "Expected EXCHANGE_TYPE_IKE_SA_INIT but received: " + exchangeType);
+                }
+
+                // Retry IKE INIT if there is Notify-Cookie
+                IkeNotifyPayload inCookiePayload = getNotifyCookie(ikeMessage);
+                if (inCookiePayload != null) {
+                    IkeNotifyPayload outCookiePayload =
+                            IkeNotifyPayload.handleCookieAndGenerateCopy(inCookiePayload);
+                    IkeMessage initReq =
+                            buildReqWithCookie(mRetransmitter.getMessage(), outCookiePayload);
+
+                    sendRequest(initReq);
+                    return;
+                }
+
+                // Negotiate IKE SA
                 validateIkeInitResp(mRetransmitter.getMessage(), ikeMessage);
 
                 mCurrentIkeSaRecord =
@@ -3140,18 +3176,45 @@
             return new IkeMessage(ikeHeader, payloadList);
         }
 
+        /**
+         * Builds an IKE INIT request that has the same payloads and SPI with the original request,
+         * and with the new Notify-Cookie Payload as the first payload.
+         */
+        private IkeMessage buildReqWithCookie(
+                IkeMessage originalReq, IkeNotifyPayload cookieNotify) {
+            List<IkePayload> payloads = new ArrayList<>();
+
+            // Notify-Cookie MUST be the first payload.
+            payloads.add(cookieNotify);
+
+            for (IkePayload payload : originalReq.ikePayloadList) {
+                // Keep all previous payloads except COOKIEs
+                if (payload instanceof IkeNotifyPayload
+                        && ((IkeNotifyPayload) payload).notifyType == NOTIFY_TYPE_COOKIE) {
+                    continue;
+                }
+                payloads.add(payload);
+            }
+
+            IkeHeader originalHeader = originalReq.ikeHeader;
+            IkeHeader header =
+                    new IkeHeader(
+                            originalHeader.ikeInitiatorSpi,
+                            originalHeader.ikeResponderSpi,
+                            PAYLOAD_TYPE_NOTIFY,
+                            IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT,
+                            false /* isResponseMsg */,
+                            true /* fromIkeInitiator */,
+                            0 /* messageId */);
+            return new IkeMessage(header, payloads);
+        }
+
         private void validateIkeInitResp(IkeMessage reqMsg, IkeMessage respMsg)
                 throws IkeProtocolException, IOException {
             IkeHeader respIkeHeader = respMsg.ikeHeader;
             mRemoteIkeSpiResource =
                     mIkeSpiGenerator.allocateSpi(mRemoteAddress, respIkeHeader.ikeResponderSpi);
 
-            int exchangeType = respIkeHeader.exchangeType;
-            if (exchangeType != IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT) {
-                throw new InvalidSyntaxException(
-                        "Expected EXCHANGE_TYPE_IKE_SA_INIT but received: " + exchangeType);
-            }
-
             IkeSaPayload respSaPayload = null;
             IkeKePayload respKePayload = null;
 
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
index 4617932..728929b 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
@@ -120,6 +120,11 @@
      */
     public static final int NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP = 16389;
     /**
+     * Might be sent by the IKE responder in an IKE_SA_INIT response, to prevent DoS Attacks. If
+     * receiving it, IKE client MUST retry IKE_SA_INIT request with the same associated data.
+     */
+    public static final int NOTIFY_TYPE_COOKIE = 16390;
+    /**
      * Indicates a willingness by its sender to use transport mode rather than tunnel mode on this
      * Child SA. Only allowed in the request/response for negotiating a Child SA.
      */
@@ -171,6 +176,9 @@
 
     private static final String NAT_DETECTION_DIGEST_ALGORITHM = "SHA-1";
 
+    private static final int COOKIE_DATA_LEN_MIN = 1;
+    private static final int COOKIE_DATA_LEN_MAX = 64;
+
     private static final int COOKIE2_DATA_LEN_MIN = 8;
     private static final int COOKIE2_DATA_LEN_MAX = 64;
 
@@ -229,6 +237,7 @@
         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, "NAT detection source IP");
         NOTIFY_TYPE_TO_STRING.put(
                 NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, "NAT detection destination IP");
+        NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE, "COOKIE");
         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode");
         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_REKEY_SA, "Rekey SA");
         NOTIFY_TYPE_TO_STRING.put(
@@ -348,16 +357,33 @@
         }
     }
 
-    /** Validate inbound Cookie2 request and build a response Cookie2 notify payload */
-    public static IkeNotifyPayload handleCookie2AndGenerateResponse(IkeNotifyPayload cookie2Notify)
-            throws InvalidSyntaxException {
+    private static IkeNotifyPayload handleCookieAndGenerateCopy(
+            IkeNotifyPayload cookie2Notify, int minLen, int maxLen) throws InvalidSyntaxException {
         byte[] notifyData = cookie2Notify.notifyData;
-        if (notifyData.length < COOKIE2_DATA_LEN_MIN || notifyData.length > COOKIE2_DATA_LEN_MAX) {
+        if (notifyData.length < minLen || notifyData.length > maxLen) {
+            String cookieType =
+                    cookie2Notify.notifyType == NOTIFY_TYPE_COOKIE2 ? "COOKIE2" : "COOKIE";
             throw new InvalidSyntaxException(
-                    "Invalid COOKIE2 notification data with length " + notifyData.length);
+                    "Invalid "
+                            + cookieType
+                            + " notification data with length "
+                            + notifyData.length);
         }
 
-        return new IkeNotifyPayload(NOTIFY_TYPE_COOKIE2, notifyData);
+        return new IkeNotifyPayload(cookie2Notify.notifyType, notifyData);
+    }
+
+    /** Validate inbound Cookie in IKE_INIT response and build a Cookie notify payload in request */
+    public static IkeNotifyPayload handleCookieAndGenerateCopy(IkeNotifyPayload cookieNotify)
+            throws InvalidSyntaxException {
+        return handleCookieAndGenerateCopy(cookieNotify, COOKIE_DATA_LEN_MIN, COOKIE_DATA_LEN_MAX);
+    }
+
+    /** Validate inbound Cookie2 request and build a response Cookie2 notify payload */
+    public static IkeNotifyPayload handleCookie2AndGenerateCopy(IkeNotifyPayload cookie2Notify)
+            throws InvalidSyntaxException {
+        return handleCookieAndGenerateCopy(
+                cookie2Notify, COOKIE2_DATA_LEN_MIN, COOKIE2_DATA_LEN_MAX);
     }
 
     /**
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
index 3b6a9bb..528bbe2 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
@@ -51,6 +51,7 @@
 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP6_PCSCF;
 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA;
 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL;
+import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_COOKIE;
 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_COOKIE2;
 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION;
 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED;
@@ -338,13 +339,16 @@
 
     private static final int PAYLOAD_TYPE_UNSUPPORTED = 127;
 
+    private static final int COOKIE_DATA_LEN = 64;
     private static final int COOKIE2_DATA_LEN = 64;
 
+    private static final byte[] COOKIE_DATA = new byte[COOKIE_DATA_LEN];
     private static final byte[] COOKIE2_DATA = new byte[COOKIE2_DATA_LEN];
 
     private static final int NATT_KEEPALIVE_DELAY = 20;
 
     static {
+        new Random().nextBytes(COOKIE_DATA);
         new Random().nextBytes(COOKIE2_DATA);
     }
 
@@ -422,31 +426,39 @@
     private ArgumentCaptor<List<IkePayload>> mPayloadListCaptor =
             ArgumentCaptor.forClass(List.class);
 
-    private ReceivedIkePacket makeDummyReceivedIkeInitRespPacket(
-            long initiatorSpi,
-            long responderSpi,
-            @IkeHeader.ExchangeType int eType,
-            boolean isResp,
-            boolean fromIkeInit,
-            List<Integer> payloadTypeList,
-            List<String> payloadHexStringList)
+    private ReceivedIkePacket makeDummyReceivedIkeInitRespPacket(List<IkePayload> payloadList)
             throws Exception {
+        long dummyInitSpi = 1L;
+        long dummyRespSpi = 2L;
 
-        List<IkePayload> payloadList =
-                hexStrListToIkePayloadList(payloadTypeList, payloadHexStringList, isResp);
         // Build a remotely generated NAT_DETECTION_SOURCE_IP payload to mock a remote node's
         // network that is not behind NAT.
         IkePayload sourceNatPayload =
                 new IkeNotifyPayload(
                         NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP,
                         IkeNotifyPayload.generateNatDetectionData(
-                                initiatorSpi,
-                                responderSpi,
+                                dummyInitSpi,
+                                dummyRespSpi,
                                 REMOTE_ADDRESS,
                                 IkeSocket.SERVER_PORT_UDP_ENCAPSULATED));
         payloadList.add(sourceNatPayload);
+
         return makeDummyUnencryptedReceivedIkePacket(
-                initiatorSpi, responderSpi, eType, isResp, fromIkeInit, payloadList);
+                dummyInitSpi,
+                dummyRespSpi,
+                IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT,
+                true /*isResp*/,
+                false /*fromIkeInit*/,
+                payloadList);
+    }
+
+    private ReceivedIkePacket makeDummyReceivedIkeInitRespPacket(
+            List<Integer> payloadTypeList, List<String> payloadHexStringList) throws Exception {
+
+        List<IkePayload> payloadList =
+                hexStrListToIkePayloadList(
+                        payloadTypeList, payloadHexStringList, true /* isResp */);
+        return makeDummyReceivedIkeInitRespPacket(payloadList);
     }
 
     private ReceivedIkePacket makeDummyUnencryptedReceivedIkePacket(
@@ -1006,16 +1018,7 @@
         payloadHexStringList.add(NONCE_RESP_PAYLOAD_HEX_STRING);
         payloadHexStringList.addAll(optionalPayloadHexStrings);
 
-        // In each test assign different IKE responder SPI in IKE INIT response to avoid remote SPI
-        // collision during response validation.
-        // STOPSHIP: b/131617794 allow #mockIkeSetup to be independent in each test after we can
-        // support IkeSession cleanup.
         return makeDummyReceivedIkeInitRespPacket(
-                1L /*initiator SPI*/,
-                2L /*responder SPI*/,
-                IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT,
-                true /*isResp*/,
-                false /*fromIkeInit*/,
                 payloadTypeList,
                 payloadHexStringList);
     }
@@ -1466,11 +1469,6 @@
         // Send back a INVALID_KE_PAYLOAD, and verify that the selected DH group changes
         ReceivedIkePacket resp =
                 makeDummyReceivedIkeInitRespPacket(
-                        1L /*initiator SPI*/,
-                        2L /*responder SPI*/,
-                        IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT,
-                        true /*isResp*/,
-                        false /*fromIkeInit*/,
                         Arrays.asList(IkePayload.PAYLOAD_TYPE_NOTIFY),
                         Arrays.asList(INVALID_KE_PAYLOAD_HEX_STRING));
         mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, resp);
@@ -1481,6 +1479,45 @@
     }
 
     @Test
+    public void testCreateIkeLocalIkeInitReceivesCookie() throws Exception {
+        setupFirstIkeSa();
+
+        mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE);
+        mLooper.dispatchAll();
+
+        // Encode 2 times: one for mIkeInitRequestBytes and one for sending packets
+        verify(mMockIkeMessageHelper, times(2)).encode(mIkeMessageCaptor.capture());
+        IkeMessage originalReqMsg = mIkeMessageCaptor.getValue();
+        List<IkePayload> originalPayloadList = originalReqMsg.ikePayloadList;
+
+        // Reset to forget sending original IKE INIT request
+        resetMockIkeMessageHelper();
+
+        // Send back a Notify-Cookie
+        IkeNotifyPayload inCookieNotify = new IkeNotifyPayload(NOTIFY_TYPE_COOKIE, COOKIE_DATA);
+        List<IkePayload> payloads = new ArrayList<>();
+        payloads.add(inCookieNotify);
+        ReceivedIkePacket resp = makeDummyReceivedIkeInitRespPacket(payloads);
+        mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, resp);
+        mLooper.dispatchAll();
+
+        // Verify retry IKE INIT request
+        verify(mMockIkeMessageHelper, times(2)).encode(mIkeMessageCaptor.capture());
+        IkeMessage ikeInitReqMessage = mIkeMessageCaptor.getValue();
+        List<IkePayload> payloadList = ikeInitReqMessage.ikePayloadList;
+
+        IkeNotifyPayload outCookieNotify = (IkeNotifyPayload) payloadList.get(0);
+        assertEquals(NOTIFY_TYPE_COOKIE, outCookieNotify.notifyType);
+        assertArrayEquals(COOKIE_DATA, outCookieNotify.notifyData);
+
+        assertEquals(originalPayloadList, payloadList.subList(1, payloadList.size()));
+
+        assertTrue(
+                mIkeSessionStateMachine.getCurrentState()
+                        instanceof IkeSessionStateMachine.CreateIkeLocalIkeInit);
+    }
+
+    @Test
     public void testCreateIkeLocalIkeInitSwitchesToEncapPorts() throws Exception {
         setupFirstIkeSa();
         mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE);
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java
index fd30297..26ed661 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java
@@ -31,6 +31,7 @@
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE;
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD;
 
+import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_COOKIE;
 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_COOKIE2;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -74,14 +75,13 @@
     private static final String PACKET_INFO_HEX_STRING =
             "4500009cafcd4000403208adc0a80064c0a800012ad4c0a200000001";
 
-    private static final int COOKIE_INVALID_DATA_LEN_SMALL = 7;
+    private static final int COOKIE_INVALID_DATA_LEN_SMALL = 0;
     private static final int COOKIE_INVALID_DATA_LEN_LARGE = 65;
     private static final int COOKIE_DATA_LEN = 64;
-    private static final byte[] COOKIE2_DATA_BYTES = new byte[COOKIE_DATA_LEN];
 
-    static {
-        new Random().nextBytes(COOKIE2_DATA_BYTES);
-    }
+    private static final int COOKIE2_INVALID_DATA_LEN_SMALL = 7;
+    private static final int COOKIE2_INVALID_DATA_LEN_LARGE = 65;
+    private static final int COOKIE2_DATA_LEN = 64;
 
     private static final String NOTIFY_REKEY_PAYLOAD_BODY_HEX_STRING = "030440092ad4c0a2";
     private static final int CHILD_SPI = 0x2ad4c0a2;
@@ -133,30 +133,50 @@
         assertArrayEquals(expectedBytes, netDetectionData);
     }
 
+    private void verifyHandleCookieAndGenerateCopy(boolean isCookie2, int dataLen)
+            throws Exception {
+        final byte[] cookieData = new byte[dataLen];
+        new Random().nextBytes(cookieData);
+        int cookieType = isCookie2 ? NOTIFY_TYPE_COOKIE2 : NOTIFY_TYPE_COOKIE;
+        IkeNotifyPayload inboundCookieNotify = new IkeNotifyPayload(cookieType, cookieData);
+
+        IkeNotifyPayload outboundCookieNotify =
+                isCookie2
+                        ? IkeNotifyPayload.handleCookie2AndGenerateCopy(inboundCookieNotify)
+                        : IkeNotifyPayload.handleCookieAndGenerateCopy(inboundCookieNotify);
+
+        assertArrayEquals(cookieData, outboundCookieNotify.notifyData);
+        assertEquals(cookieType, outboundCookieNotify.notifyType);
+    }
+
     @Test
-    public void testHandleAndReplyCookie2Request() throws Exception {
-        IkeNotifyPayload cookie2Req = new IkeNotifyPayload(NOTIFY_TYPE_COOKIE2, COOKIE2_DATA_BYTES);
-        IkeNotifyPayload cookie2Resp =
-                IkeNotifyPayload.handleCookie2AndGenerateResponse(cookie2Req);
-        assertArrayEquals(COOKIE2_DATA_BYTES, cookie2Resp.notifyData);
-        assertEquals(NOTIFY_TYPE_COOKIE2, cookie2Resp.notifyType);
-    }
-
-    private void checkHandleCookie2Request(int dataLen) throws Exception {
-        final byte[] invalidCookie2Data = new byte[dataLen];
-        new Random().nextBytes(invalidCookie2Data);
-        IkeNotifyPayload cookie2Req = new IkeNotifyPayload(NOTIFY_TYPE_COOKIE2, invalidCookie2Data);
-        IkeNotifyPayload.handleCookie2AndGenerateResponse(cookie2Req);
+    public void testHandleCookieAndGenerateCopy() throws Exception {
+        verifyHandleCookieAndGenerateCopy(false /* isCookie2 */, COOKIE_DATA_LEN);
     }
 
     @Test(expected = InvalidSyntaxException.class)
-    public void testHandleCookie2RequestWithTooSmallLengthOfData() throws Exception {
-        checkHandleCookie2Request(COOKIE_INVALID_DATA_LEN_SMALL);
+    public void testHandleCookieWithTooSmallLengthOfData() throws Exception {
+        verifyHandleCookieAndGenerateCopy(false /* isCookie2 */, COOKIE_INVALID_DATA_LEN_SMALL);
     }
 
     @Test(expected = InvalidSyntaxException.class)
-    public void testHandleCookie2RequestWithTooLargeLengthOfData() throws Exception {
-        checkHandleCookie2Request(COOKIE_INVALID_DATA_LEN_LARGE);
+    public void testHandleCookieWithTooLargeLengthOfData() throws Exception {
+        verifyHandleCookieAndGenerateCopy(false /* isCookie2 */, COOKIE_INVALID_DATA_LEN_SMALL);
+    }
+
+    @Test
+    public void testHandleCookie2AndGenerateCopy() throws Exception {
+        verifyHandleCookieAndGenerateCopy(true /* isCookie2 */, COOKIE2_DATA_LEN);
+    }
+
+    @Test(expected = InvalidSyntaxException.class)
+    public void testHandleCookie2WithTooSmallLengthOfData() throws Exception {
+        verifyHandleCookieAndGenerateCopy(true /* isCookie2 */, COOKIE2_INVALID_DATA_LEN_SMALL);
+    }
+
+    @Test(expected = InvalidSyntaxException.class)
+    public void testHandleCookie2WithTooLargeLengthOfData() throws Exception {
+        verifyHandleCookieAndGenerateCopy(true /* isCookie2 */, COOKIE2_INVALID_DATA_LEN_SMALL);
     }
 
     @Test