Merge "Fix bug in outbound fragment integrity checksum"
diff --git a/Android.bp b/Android.bp
index 8e7717a..f954c1c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -98,6 +98,7 @@
         "framework-module-api-defaults-module_libs_api",
         "ike-stubs-defaults",
     ],
+    dist: { dest: "android.net.ipsec.ike.txt" },
 }
 
 droidstubs {
@@ -112,4 +113,5 @@
     name: "android.net.ipsec.ike.stubs.module_libs_api",
     srcs: [":android.net.ipsec.ike.stubs.sources.module_libs_api"],
     defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
+    dist: { dest: "android.net.ipsec.ike.jar" },
 }
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
new file mode 100644
index 0000000..bec7683
--- /dev/null
+++ b/api/system-lint-baseline.txt
@@ -0,0 +1,75 @@
+// Baseline format: 1.0
+BuilderSetStyle: android.net.ipsec.ike.IkeSessionParams.Builder#removeIkeOption(int):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.ipsec.ike.IkeSessionParams.Builder.removeIkeOption(int)
+
+
+CallbackInterface: android.net.ipsec.ike.ChildSessionCallback:
+    Callbacks must be abstract class instead of interface to enable extension in future API levels: ChildSessionCallback
+CallbackInterface: android.net.ipsec.ike.IkeSessionCallback:
+    Callbacks must be abstract class instead of interface to enable extension in future API levels: IkeSessionCallback
+
+
+MethodNameUnits: android.net.ipsec.ike.IkeSessionParams#getDpdDelaySeconds():
+    Returned time values must be in milliseconds, was `getDpdDelaySeconds`
+
+
+MissingGetterMatchingBuilder: android.net.eap.EapSessionConfig.Builder#setEapMsChapV2Config(String, String):
+    android.net.eap.EapSessionConfig does not declare a `getEapMsChapV2Config()` method matching method android.net.eap.EapSessionConfig.Builder.setEapMsChapV2Config(String,String)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addDhGroup(int):
+    android.net.ipsec.ike.ChildSaProposal does not declare a `getDhGroups()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addDhGroup(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addEncryptionAlgorithm(int, int):
+    android.net.ipsec.ike.ChildSaProposal does not declare a `getEncryptionAlgorithms()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addEncryptionAlgorithm(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addIntegrityAlgorithm(int):
+    android.net.ipsec.ike.ChildSaProposal does not declare a `getIntegrityAlgorithms()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addIntegrityAlgorithm(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addDhGroup(int):
+    android.net.ipsec.ike.IkeSaProposal does not declare a `getDhGroups()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addDhGroup(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addEncryptionAlgorithm(int, int):
+    android.net.ipsec.ike.IkeSaProposal does not declare a `getEncryptionAlgorithms()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addEncryptionAlgorithm(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addIntegrityAlgorithm(int):
+    android.net.ipsec.ike.IkeSaProposal does not declare a `getIntegrityAlgorithms()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addIntegrityAlgorithm(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addIkeOption(int):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getIkeOptions()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addIkeOption(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addPcscfServerRequest(int):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getPcscfServerRequests()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addPcscfServerRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addPcscfServerRequest(java.net.InetAddress):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getPcscfServerRequests()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addPcscfServerRequest(java.net.InetAddress)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthDigitalSignature(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.security.PrivateKey):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthDigitalSignature()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthDigitalSignature(java.security.cert.X509Certificate,java.security.cert.X509Certificate,java.security.PrivateKey)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthDigitalSignature(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.util.List<java.security.cert.X509Certificate>, java.security.PrivateKey):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthDigitalSignature()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthDigitalSignature(java.security.cert.X509Certificate,java.security.cert.X509Certificate,java.util.List<java.security.cert.X509Certificate>,java.security.PrivateKey)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthEap(java.security.cert.X509Certificate, android.net.eap.EapSessionConfig):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthEap()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthEap(java.security.cert.X509Certificate,android.net.eap.EapSessionConfig)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthPsk(byte[]):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthPsk()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthPsk(byte[])
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setLifetimeSeconds(int, int):
+    android.net.ipsec.ike.IkeSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setLifetimeSeconds(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+    android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getInboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+    android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getOutboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addSaProposal(android.net.ipsec.ike.ChildSaProposal):
+    android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getSaProposals()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addSaProposal(android.net.ipsec.ike.ChildSaProposal)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#setLifetimeSeconds(int, int):
+    android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.setLifetimeSeconds(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(int):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(java.net.Inet4Address):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(java.net.Inet4Address)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(java.net.Inet6Address, int):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(java.net.Inet6Address,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalDhcpServerRequest(int):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalDhcpServerRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalDhcpServerRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalDnsServerRequest(int):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalDnsServerRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalDnsServerRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getOutboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addSaProposal(android.net.ipsec.ike.ChildSaProposal):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getSaProposals()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addSaProposal(android.net.ipsec.ike.ChildSaProposal)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#setLifetimeSeconds(int, int):
+    android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.setLifetimeSeconds(int,int)
+
+
+MissingNullability: android.net.ipsec.ike.IkeSessionParams#getRetransmissionTimeoutsMillis():
+    Missing nullability on method `getRetransmissionTimeoutsMillis` return
diff --git a/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java b/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java
index 10a23db..c8d31f6 100644
--- a/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java
+++ b/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java
@@ -95,29 +95,10 @@
     }
 
     /** Represents an IPv4 DHCP server request */
-    public interface ConfigRequestIpv4DhcpServer extends TunnelModeChildConfigRequest {
-        /**
-         * Retrieves the requested IPv4 DHCP server address
-         *
-         * @return The requested DHCP server address, or null if no specific DHCP server was
-         *     requested
-         * @hide
-         */
-        @Nullable
-        Inet4Address getAddress();
-    }
+    public interface ConfigRequestIpv4DhcpServer extends TunnelModeChildConfigRequest {}
 
     /** Represents an IPv4 DNS Server request */
-    public interface ConfigRequestIpv4DnsServer extends TunnelModeChildConfigRequest {
-        /**
-         * Retrieves the requested IPv4 DNS server address
-         *
-         * @return The requested DNS server address, or null if no specific DNS server was requested
-         * @hide
-         */
-        @Nullable
-        Inet4Address getAddress();
-    }
+    public interface ConfigRequestIpv4DnsServer extends TunnelModeChildConfigRequest {}
 
     /** Represents an IPv4 Netmask request */
     public interface ConfigRequestIpv4Netmask extends TunnelModeChildConfigRequest {}
@@ -141,16 +122,7 @@
     }
 
     /** Represents an IPv6 DNS Server request */
-    public interface ConfigRequestIpv6DnsServer extends TunnelModeChildConfigRequest {
-        /**
-         * Retrieves the requested IPv6 DNS server address
-         *
-         * @return The requested DNS server address, or null if no specific DNS server was requested
-         * @hide
-         */
-        @Nullable
-        Inet6Address getAddress();
-    }
+    public interface ConfigRequestIpv6DnsServer extends TunnelModeChildConfigRequest {}
 
     /** This class can be used to incrementally construct a {@link TunnelModeChildSessionParams}. */
     public static final class Builder extends ChildSessionParams.Builder {
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java b/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java
index e86df23..f8008fb 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java
@@ -64,15 +64,18 @@
      * Notifies the scheduler that the caller is ready for a new procedure
      *
      * <p>Synchronously triggers the call to onNewProcedureReady.
+     *
+     * @return whether or not a new procedure was scheduled.
      */
-    public void readyForNextProcedure() {
+    public boolean readyForNextProcedure() {
         while (!mRequestQueue.isEmpty()) {
             LocalRequest request = mRequestQueue.poll();
             if (!request.isCancelled()) {
                 mConsumer.onNewProcedureReady(request);
-                return;
+                return true;
             }
         }
+        return false;
     }
 
     /**
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 ec6d26a..ea496f8 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -22,6 +22,7 @@
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS;
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ErrorType;
+import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
 
 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_TYPE_REPLY;
 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL;
@@ -74,6 +75,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -365,6 +367,9 @@
      */
     private final IpSecSpiGenerator mIpSecSpiGenerator;
 
+    /** Ensures that the system does not go to sleep in the middle of an exchange. */
+    private final PowerManager.WakeLock mBusyWakeLock;
+
     @VisibleForTesting
     @GuardedBy("mChildCbToSessions")
     final HashMap<ChildSessionCallback, ChildSessionStateMachine> mChildCbToSessions =
@@ -492,6 +497,10 @@
             // callback instance in the future
         }
 
+        PowerManager pm = context.getSystemService(PowerManager.class);
+        mBusyWakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, TAG + "mBusyWakeLock");
+        mBusyWakeLock.setReferenceCounted(false);
+
         mIkeSessionId = sIkeSessionIdGenerator.getAndIncrement();
         sIkeAlarmReceiver.registerIkeSession(mIkeSessionId, getHandler());
 
@@ -548,6 +557,7 @@
                             sendMessageAtFrontOfQueue(CMD_EXECUTE_LOCAL_REQ, localReq);
                         });
 
+        mBusyWakeLock.acquire();
         start();
     }
 
@@ -956,6 +966,8 @@
             }
             // TODO: Remove the stored ikeSessionCallback
         }
+
+        mBusyWakeLock.release();
     }
 
     private void closeAllSaRecords(boolean expectSaClosed) {
@@ -1058,7 +1070,9 @@
 
         @Override
         public void enterState() {
-            mScheduler.readyForNextProcedure();
+            if (!mScheduler.readyForNextProcedure()) {
+                mBusyWakeLock.release();
+            }
 
             if (mDpdIntent == null) {
                 long remoteIkeSpi = mCurrentIkeSaRecord.getRemoteSpi();
@@ -1090,6 +1104,8 @@
             // #exitState is guaranteed to be invoked when quit() or quitNow() is called
             mAlarmManager.cancel(mDpdIntent);
             logd("DPD Alarm canceled");
+
+            mBusyWakeLock.acquire();
         }
 
         @Override
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java
new file mode 100644
index 0000000..92f3207
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 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.internal.net.ipsec.ike.message;
+
+import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CERT_ENCODING_LEN;
+import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CertificateEncoding;
+
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This class represents a Certificate Request Payload
+ *
+ * <p>A Certificate Request Payload provides suggestion for an end certificate to select. Receiver
+ * of this payload is allowed to send an alternate. It is possible that there is a preferred CA sent
+ * in the IkeCertReqPayload, but an alternate is still acceptable.
+ *
+ * <p>IKE library will always ignore this payload since only one end certificate can be configured
+ * by users.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange
+ *     Protocol Version 2 (IKEv2)</a>
+ */
+public class IkeCertReqPayload extends IkePayload {
+    /** Certificate encoding type */
+    @CertificateEncoding public final int certEncodingType;
+    /** Concatenated list of SHA-1 hashes of CAs' Subject Public Key Info */
+    public final byte[] caSubjectPublicKeyInforHashes;
+
+    /**
+     * Construct an instance of IkeCertReqPayload from decoding an inbound IKE packet.
+     *
+     * <p>NegativeArraySizeException and BufferUnderflowException will be caught in {@link
+     * IkeMessage}
+     *
+     * @param critical indicates if this payload is critical. Ignored in supported payload as
+     *     instructed by the RFC 7296.
+     * @param payloadBody payload body in byte array
+     * @throws IkeProtocolException if there is any error
+     */
+    public IkeCertReqPayload(boolean critical, byte[] payloadBody) throws IkeProtocolException {
+        super(PAYLOAD_TYPE_CERT_REQUEST, critical);
+
+        ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
+        certEncodingType = Byte.toUnsignedInt(inputBuffer.get());
+        caSubjectPublicKeyInforHashes = new byte[inputBuffer.remaining()];
+        inputBuffer.get(caSubjectPublicKeyInforHashes);
+    }
+
+    /**
+     * Encode Certificate Request Payload to ByteBuffer.
+     *
+     * @param nextPayload type of payload that follows this payload.
+     * @param byteBuffer destination ByteBuffer that stores encoded payload.
+     */
+    @Override
+    protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
+        encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
+
+        byteBuffer.put((byte) certEncodingType).put(caSubjectPublicKeyInforHashes);
+    }
+
+    /**
+     * Get entire payload length.
+     *
+     * @return entire payload length.
+     */
+    @Override
+    protected int getPayloadLength() {
+        return GENERIC_HEADER_LENGTH + CERT_ENCODING_LEN + caSubjectPublicKeyInforHashes.length;
+    }
+
+    /**
+     * Return the payload type as a String.
+     *
+     * @return the payload type as a String.
+     */
+    @Override
+    public String getTypeString() {
+        return "CertReq";
+    }
+}
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java
index bb5f9a3..778faa4 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java
@@ -538,7 +538,6 @@
             super(CONFIG_ATTR_INTERNAL_IP4_DHCP, value);
         }
 
-        @Override
         public Inet4Address getAddress() {
             return address;
         }
@@ -572,7 +571,6 @@
             super(CONFIG_ATTR_INTERNAL_IP4_DNS, value);
         }
 
-        @Override
         public Inet4Address getAddress() {
             return address;
         }
@@ -979,7 +977,6 @@
             super(CONFIG_ATTR_INTERNAL_IP6_DNS, value);
         }
 
-        @Override
         public Inet6Address getAddress() {
             return address;
         }
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
index 2f191d4..b50f093 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
@@ -60,7 +60,6 @@
                 int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody)
                 throws IkeProtocolException {
             switch (payloadType) {
-                    // TODO: Add cases for creating supported payloads.
                 case IkePayload.PAYLOAD_TYPE_SA:
                     return new IkeSaPayload(isCritical, isResp, payloadBody);
                 case IkePayload.PAYLOAD_TYPE_KE:
@@ -71,6 +70,8 @@
                     return new IkeIdPayload(isCritical, payloadBody, false);
                 case IkePayload.PAYLOAD_TYPE_CERT:
                     return IkeCertPayload.getIkeCertPayload(isCritical, payloadBody);
+                case IkeCertReqPayload.PAYLOAD_TYPE_CERT_REQUEST:
+                    return new IkeCertReqPayload(isCritical, payloadBody);
                 case IkePayload.PAYLOAD_TYPE_AUTH:
                     return IkeAuthPayload.getIkeAuthPayload(isCritical, payloadBody);
                 case IkePayload.PAYLOAD_TYPE_NONCE:
diff --git a/tests/iketests/AndroidManifest.xml b/tests/iketests/AndroidManifest.xml
index 0abddba..bdcfdb7 100644
--- a/tests/iketests/AndroidManifest.xml
+++ b/tests/iketests/AndroidManifest.xml
@@ -22,6 +22,8 @@
     <uses-permission android:name="android.permission.INTERNET"/>
     <!--Allow tests to call ConnectivityManager#getActiveNetwork()-->
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <!--Allow tests to use wake locks-->
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
 
     <!--
        'debuggable=true' is required to properly load mockito jvmti dependencies,
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 61f44e0..66c2517 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
@@ -1196,6 +1196,7 @@
         mLooper.dispatchAll();
 
         verify(mSpyCurrentIkeSocket).releaseReference(eq(mIkeSessionStateMachine));
+        verify(mMockBusyWakelock).release();
     }
 
     @Test
@@ -1457,6 +1458,8 @@
 
     /** Initializes the mIkeSessionStateMachine in the IDLE state. */
     private void setupIdleStateMachine() throws Exception {
+        verify(mMockBusyWakelock).acquire();
+
         setIkeInitResults();
 
         mIkeSessionStateMachine.sendMessage(
@@ -1469,6 +1472,11 @@
 
         assertTrue(
                 mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
+
+        verify(mMockBusyWakelock).release();
+
+        // For convenience to verify wakelocks in all other places.
+        reset(mMockBusyWakelock);
     }
 
     private void mockIkeInitAndTransitionToIkeAuth(State authState) throws Exception {
@@ -1630,6 +1638,7 @@
                 mIkeSessionStateMachine.getCurrentState()
                         instanceof IkeSessionStateMachine.ChildProcedureOngoing);
         verify(mMockChildSessionStateMachine).deleteChildSession();
+        verify(mMockBusyWakelock).acquire();
     }
 
     @Test
@@ -1664,6 +1673,7 @@
                 mIkeSessionStateMachine.getCurrentState()
                         instanceof IkeSessionStateMachine.ChildProcedureOngoing);
         verify(mMockChildSessionStateMachine).rekeyChildSession();
+        verify(mMockBusyWakelock).acquire();
     }
 
     @Test
@@ -1803,6 +1813,7 @@
         List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/);
         assertEquals(1, payloadList.size());
         assertEquals(outDelPayload, ((IkeDeletePayload) payloadList.get(0)));
+        verify(mMockBusyWakelock).acquire();
     }
 
     @Test
@@ -4108,6 +4119,7 @@
 
         // Verify state machine quit properly
         assertNull(mIkeSessionStateMachine.getCurrentState());
+        verify(mMockBusyWakelock).release();
     }
 
     @Test
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
index 174cd7c..e4e8379 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
@@ -18,8 +18,11 @@
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
@@ -33,6 +36,7 @@
 import android.net.NetworkCapabilities;
 import android.net.SocketKeepalive;
 import android.os.Handler;
+import android.os.PowerManager;
 
 import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils;
 import com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver;
@@ -50,9 +54,12 @@
             (Inet4Address) (InetAddresses.parseNumericAddress("127.0.0.1"));
     protected static final String REMOTE_HOSTNAME = "ike.test.android.com";
 
+    protected PowerManager.WakeLock mMockBusyWakelock;
+
     protected MockIpSecTestUtils mMockIpSecTestUtils;
     protected Context mSpyContext;
     protected IpSecManager mIpSecManager;
+    protected PowerManager mPowerManager;
 
     protected ConnectivityManager mMockConnectManager;
     protected Network mMockDefaultNetwork;
@@ -74,6 +81,11 @@
                         any(Handler.class));
         doNothing().when(mSpyContext).unregisterReceiver(any(IkeAlarmReceiver.class));
 
+        mPowerManager = mock(PowerManager.class);
+        mMockBusyWakelock = mock(PowerManager.WakeLock.class);
+        doReturn(mPowerManager).when(mSpyContext).getSystemService(eq(PowerManager.class));
+        doReturn(mMockBusyWakelock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+
         mMockConnectManager = mock(ConnectivityManager.class);
         mMockDefaultNetwork = mock(Network.class);
         doReturn(mMockDefaultNetwork).when(mMockConnectManager).getActiveNetwork();
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java
new file mode 100644
index 0000000..f859a82
--- /dev/null
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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.internal.net.ipsec.ike.message;
+
+import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CERTIFICATE_ENCODING_X509_CERT_SIGNATURE;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.util.Pair;
+
+import com.android.internal.net.TestUtils;
+
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+public class IkeCertReqPayloadTest {
+    private static final int CERT_ENCODING_TYPE = CERTIFICATE_ENCODING_X509_CERT_SIGNATURE;
+    private static final int NEXT_PAYLOAD_TYPE = IkePayload.PAYLOAD_TYPE_AUTH;
+    private static final byte[] CERT_REQ_PAYLOAD =
+            TestUtils.hexStringToByteArray("27000019040d0a12bb1f98996563f15b10db95c67eea7990fa");
+    private static final byte[] CA_SUBJECT_PUBLIC_KEY_INFO_HASH =
+            TestUtils.hexStringToByteArray("0d0a12bb1f98996563f15b10db95c67eea7990fa");
+
+    @Test
+    public void testDecode() throws Exception {
+        Pair<IkePayload, Integer> pair =
+                IkePayloadFactory.getIkePayload(
+                        IkePayload.PAYLOAD_TYPE_CERT_REQUEST,
+                        false /*isResp*/,
+                        ByteBuffer.wrap(CERT_REQ_PAYLOAD));
+
+        IkeCertReqPayload certPayload = (IkeCertReqPayload) pair.first;
+        assertEquals(CERT_ENCODING_TYPE, certPayload.certEncodingType);
+        assertArrayEquals(
+                CA_SUBJECT_PUBLIC_KEY_INFO_HASH, certPayload.caSubjectPublicKeyInforHashes);
+
+        assertEquals(NEXT_PAYLOAD_TYPE, (int) pair.second);
+    }
+
+    @Test
+    public void testEncode() throws Exception {
+        Pair<IkePayload, Integer> pair =
+                IkePayloadFactory.getIkePayload(
+                        IkePayload.PAYLOAD_TYPE_CERT_REQUEST,
+                        false /*isResp*/,
+                        ByteBuffer.wrap(CERT_REQ_PAYLOAD));
+        IkeCertReqPayload certPayload = (IkeCertReqPayload) pair.first;
+
+        ByteBuffer byteBuffer = ByteBuffer.allocate(CERT_REQ_PAYLOAD.length);
+        certPayload.encodeToByteBuffer(NEXT_PAYLOAD_TYPE, byteBuffer);
+        assertArrayEquals(CERT_REQ_PAYLOAD, byteBuffer.array());
+
+        assertEquals(CERT_REQ_PAYLOAD.length, certPayload.getPayloadLength());
+    }
+}