Merge "Revert "Move frameworks/base/Android.mk to build/tasks"" into main
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5713e9e..a4b16f8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8158,6 +8158,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
+        <service android:name="com.android.system.virtualmachine.SecretkeeperJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
         <service android:name="com.android.server.PruneInstantAppsJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 61e6a36..7d740ef 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -88,6 +88,9 @@
     <!-- Colombia: 1-6 digits (not confirmed) -->
     <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289" />
 
+    <!-- Costa Rica  -->
+    <shortcode country="cr" pattern="\\d{1,6}" free="466453" />
+
     <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
 
@@ -143,6 +146,9 @@
     <!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece -->
     <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}|12115" />
 
+    <!--  Guatemala  -->
+    <shortcode country="gt" pattern="\\d{1,6}" free="466453" />
+
     <!-- Croatia -->
     <shortcode country="hr" pattern="\\d{1,5}" free="13062" />
 
@@ -241,10 +247,10 @@
     <shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
 
     <!-- Pakistan -->
-    <shortcode country="pk" pattern="\\d{1,5}" free="2057" />
+    <shortcode country="pk" pattern="\\d{1,5}" free="2057|9092" />
 
     <!-- Palestine: 5 digits, known premium codes listed -->
-    <shortcode country="ps" pattern="\\d{1,5}" free="37477" />
+    <shortcode country="ps" pattern="\\d{1,5}" free="37477|6681" />
 
     <!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012|80921" />
@@ -324,4 +330,7 @@
     <!-- South Africa -->
     <shortcode country="za" pattern="\\d{1,5}" free="44136|30791|36056" />
 
+    <!-- Zimbabwe -->
+    <shortcode country="zw" pattern="\\d{1,5}" free="33679" />
+
 </shortcodes>
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 3f7f088..51100c0 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -29,11 +29,13 @@
 import com.android.internal.util.Preconditions;
 
 import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.NeverInline;
 
 import libcore.util.NativeAllocationRegistry;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
 import java.util.Objects;
 
 /**
@@ -83,6 +85,30 @@
         return mChars;
     }
 
+    private void rangeCheck(int start, int end) {
+        if (start < 0 || start > end || end > mChars.length) {
+            throwRangeError(start, end);
+        }
+    }
+
+    @NeverInline
+    private void throwRangeError(int start, int end) {
+        throw new IllegalArgumentException(String.format(Locale.US,
+            "start(%d) end(%d) length(%d) out of bounds", start, end, mChars.length));
+    }
+
+    private void offsetCheck(int offset) {
+        if (offset < 0 || offset >= mChars.length) {
+            throwOffsetError(offset);
+        }
+    }
+
+    @NeverInline
+    private void throwOffsetError(int offset) {
+        throw new IllegalArgumentException(String.format(Locale.US,
+            "offset (%d) length(%d) out of bounds", offset, mChars.length));
+    }
+
     /**
      * Returns the width of a given range.
      *
@@ -91,12 +117,7 @@
      */
     public @FloatRange(from = 0.0) @Px float getWidth(
             @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        Preconditions.checkArgument(0 <= start && start <= mChars.length,
-                "start(%d) must be 0 <= start <= %d", start, mChars.length);
-        Preconditions.checkArgument(0 <= end && end <= mChars.length,
-                "end(%d) must be 0 <= end <= %d", end, mChars.length);
-        Preconditions.checkArgument(start <= end,
-                "start(%d) is larger than end(%d)", start, end);
+        rangeCheck(start, end);
         return nGetWidth(mNativePtr, start, end);
     }
 
@@ -118,12 +139,7 @@
      */
     public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull Rect rect) {
-        Preconditions.checkArgument(0 <= start && start <= mChars.length,
-                "start(%d) must be 0 <= start <= %d", start, mChars.length);
-        Preconditions.checkArgument(0 <= end && end <= mChars.length,
-                "end(%d) must be 0 <= end <= %d", end, mChars.length);
-        Preconditions.checkArgument(start <= end,
-                "start(%d) is larger than end(%d)", start, end);
+        rangeCheck(start, end);
         Preconditions.checkNotNull(rect);
         nGetBounds(mNativePtr, mChars, start, end, rect);
     }
@@ -137,12 +153,7 @@
      */
     public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull Paint.FontMetricsInt outMetrics) {
-        Preconditions.checkArgument(0 <= start && start <= mChars.length,
-                "start(%d) must be 0 <= start <= %d", start, mChars.length);
-        Preconditions.checkArgument(0 <= end && end <= mChars.length,
-                "end(%d) must be 0 <= end <= %d", end, mChars.length);
-        Preconditions.checkArgument(start <= end,
-                "start(%d) is larger than end(%d)", start, end);
+        rangeCheck(start, end);
         Objects.requireNonNull(outMetrics);
 
         long packed = nGetExtent(mNativePtr, mChars, start, end);
@@ -158,8 +169,7 @@
      * @param offset an offset of the character.
      */
     public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) {
-        Preconditions.checkArgument(0 <= offset && offset < mChars.length,
-                "offset(%d) is larger than text length %d" + offset, mChars.length);
+        offsetCheck(offset);
         return nGetCharWidthAt(mNativePtr, offset);
     }
 
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 2795cfe..f05ea9c 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -336,8 +336,9 @@
      * This method must not be called when the Visualizer is enabled.
      * @param size requested capture size
      * @return {@link #SUCCESS} in case of success,
-     * {@link #ERROR_BAD_VALUE} in case of failure.
-     * @throws IllegalStateException
+     * {@link #ERROR_INVALID_OPERATION} if Visualizer effect enginer not enabled.
+     * @throws IllegalStateException if the effect is not in proper state.
+     * @throws IllegalArgumentException if the size parameter is invalid (out of supported range).
      */
     public int setCaptureSize(int size)
     throws IllegalStateException {
@@ -345,7 +346,13 @@
             if (mState != STATE_INITIALIZED) {
                 throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
             }
-            return native_setCaptureSize(size);
+
+            int ret = native_setCaptureSize(size);
+            if (ret == ERROR_BAD_VALUE) {
+                throw(new IllegalArgumentException("setCaptureSize to " + size + " failed"));
+            }
+
+            return ret;
         }
     }
 
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index f8dd756..6223acf 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -24,7 +24,10 @@
 import java.util.Objects;
 
 /**
- * An instances of this class represents a session of media playback.
+ * An instance of this class represents a session of media playback used to report playback
+ * metrics and events.
+ *
+ * Create a new instance using {@link MediaMetricsManager#createPlaybackSession}.
  */
 public final class PlaybackSession implements AutoCloseable {
     private final @NonNull String mId;
@@ -80,6 +83,21 @@
         mManager.reportTrackChangeEvent(mId, event);
     }
 
+    /**
+     * A session ID is used to identify a unique playback and to tie together lower-level
+     * playback components.
+     *
+     * Associate this session with a {@link MediaCodec} by passing the ID into
+     * {@link MediaFormat} through {@link MediaFormat#LOG_SESSION_ID} when
+     * creating the {@link MediaCodec}.
+     *
+     * Associate this session with an {@link AudioTrack} by calling
+     * {@link AudioTrack#setLogSessionId}.
+     *
+     * Associate this session with {@link MediaDrm} and {@link MediaCrypto} by calling
+     * {@link MediaDrm#getPlaybackComponent} and then calling
+     * {@link PlaybackComponent#setLogSessionId}.
+     */
     public @NonNull LogSessionId getSessionId() {
         return mLogSessionId;
     }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 8396005..0fc80dd 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2895,6 +2895,10 @@
         jint offset,
         jint size,
         std::shared_ptr<C2Buffer> *buffer) {
+    if ((offset + size) > context->capacity()) {
+        ALOGW("extractBufferFromContext: offset + size provided exceed capacity");
+        return;
+    }
     *buffer = context->toC2Buffer(offset, size);
     if (*buffer == nullptr) {
         if (!context->mMemory) {
@@ -2995,18 +2999,15 @@
                     "MediaCodec.LinearBlock#obtain method to obtain a compatible buffer.");
             return;
         }
-        sp<CryptoInfosWrapper> cryptoInfos = new CryptoInfosWrapper{decltype(cryptoInfos->value)()};
-        jint sampleSize = 0;
+        sp<CryptoInfosWrapper> cryptoInfos = nullptr;
+        jint sampleSize = totalSize;
         if (cryptoInfoArray != nullptr) {
+            cryptoInfos = new CryptoInfosWrapper{decltype(cryptoInfos->value)()};
             extractCryptoInfosFromObjectArray(env,
                     &sampleSize,
                     &cryptoInfos->value,
                     cryptoInfoArray,
                     &errorDetailMsg);
-        } else {
-            sampleSize = totalSize;
-            std::unique_ptr<CodecCryptoInfo> cryptoInfo{new MediaCodecCryptoInfo(totalSize)};
-            cryptoInfos->value.push_back(std::move(cryptoInfo));
         }
         if (env->ExceptionCheck()) {
             // Creation of cryptoInfo failed. Let the exception bubble up.
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 09c45ea..9ae5c99 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -25,7 +25,6 @@
 #include <limits.h>
 
 #include <audio_utils/fixedfft.h>
-#include <cutils/bitops.h>
 #include <utils/Thread.h>
 
 #include <android/content/AttributionSourceState.h>
@@ -59,8 +58,8 @@
     status_t status = AudioEffect::set(
             SL_IID_VISUALIZATION, nullptr, priority, cbf, user, sessionId, io, device, probe);
     if (status == NO_ERROR || status == ALREADY_EXISTS) {
-        initCaptureSize();
-        initSampleRate();
+        status = initCaptureSize();
+        if (status == NO_ERROR) initSampleRate();
     }
     return status;
 }
@@ -152,9 +151,8 @@
 
 status_t Visualizer::setCaptureSize(uint32_t size)
 {
-    if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
-        size < VISUALIZER_CAPTURE_SIZE_MIN ||
-        popcount(size) != 1) {
+    if (!isCaptureSizeValid(size)) {
+        ALOGE("%s with invalid capture size %u from HAL", __func__, size);
         return BAD_VALUE;
     }
 
@@ -172,7 +170,7 @@
     *((int32_t *)p->data + 1)= size;
     status_t status = setParameter(p);
 
-    ALOGV("setCaptureSize size %d  status %d p->status %d", size, status, p->status);
+    ALOGV("setCaptureSize size %u status %d p->status %d", size, status, p->status);
 
     if (status == NO_ERROR) {
         status = p->status;
@@ -257,8 +255,8 @@
     if ((type != MEASUREMENT_MODE_PEAK_RMS)
             // for peak+RMS measurement, the results are 2 int32_t values
             || (number != 2)) {
-        ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
-                        number);
+        ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %u",
+              number);
         return BAD_VALUE;
     }
 
@@ -390,7 +388,7 @@
     }
 }
 
-uint32_t Visualizer::initCaptureSize()
+status_t Visualizer::initCaptureSize()
 {
     uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
     effect_param_t *p = (effect_param_t *)buf32;
@@ -405,14 +403,20 @@
     }
 
     uint32_t size = 0;
-    if (status == NO_ERROR) {
-        size = *((int32_t *)p->data + 1);
+    if (status != NO_ERROR) {
+        ALOGE("%s getParameter failed status %d", __func__, status);
+        return status;
     }
+
+    size = *((int32_t *)p->data + 1);
+    if (!isCaptureSizeValid(size)) {
+        ALOGE("%s with invalid capture size %u from HAL", __func__, size);
+        return BAD_VALUE;
+    }
+
     mCaptureSize = size;
-
-    ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
-
-    return size;
+    ALOGV("%s size %u status %d", __func__, mCaptureSize, status);
+    return NO_ERROR;
 }
 
 void Visualizer::initSampleRate()
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index b38c01f..26d58d0 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -20,6 +20,8 @@
 #include <media/AudioEffect.h>
 #include <system/audio_effects/effect_visualizer.h>
 #include <utils/Thread.h>
+#include <cstdint>
+#include <cutils/bitops.h>
 #include "android/content/AttributionSourceState.h"
 
 /**
@@ -170,8 +172,12 @@
 
     status_t doFft(uint8_t *fft, uint8_t *waveform);
     void periodicCapture();
-    uint32_t initCaptureSize();
+    status_t initCaptureSize();
     void initSampleRate();
+    static constexpr bool isCaptureSizeValid(uint32_t size) {
+        return size <= VISUALIZER_CAPTURE_SIZE_MAX && size >= VISUALIZER_CAPTURE_SIZE_MIN &&
+                popcount(size) == 1;
+    }
 
     Mutex mCaptureLock;
     uint32_t mCaptureRate = CAPTURE_RATE_DEF;
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index b6ba6a5..5320033 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -84,15 +84,6 @@
         {
             "name": "CtsVcnTestCases",
             "file_patterns": ["VcnManagementService\\.java"]
-        },
-        {
-            "name": "FrameworksNetTests",
-            "options": [
-                {
-                    "exclude-annotation": "com.android.testutils.SkipPresubmit"
-                }
-            ],
-            "file_patterns": ["VpnManagerService\\.java"]
         }
     ],
     "presubmit-large": [
@@ -126,5 +117,10 @@
             ],
             "file_patterns": ["VpnManagerService\\.java"]
         }
+    ],
+    "postsubmit": [
+        {
+            "name": "FrameworksVpnTests"
+        }
     ]
 }
diff --git a/services/core/java/com/android/server/connectivity/TEST_MAPPING b/services/core/java/com/android/server/connectivity/TEST_MAPPING
index 687d4b0..c2b1c77 100644
--- a/services/core/java/com/android/server/connectivity/TEST_MAPPING
+++ b/services/core/java/com/android/server/connectivity/TEST_MAPPING
@@ -7,7 +7,7 @@
                   "exclude-annotation": "com.android.testutils.SkipPresubmit"
               }
           ],
-          "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
+          "file_patterns": ["VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
       }
   ],
   "presubmit-large": [
@@ -26,5 +26,10 @@
           ],
         "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
       }
+  ],
+  "postsubmit":[
+      {
+          "name":"FrameworksVpnTests"
+      }
   ]
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index e0376ed..8db5905 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -28,5 +28,10 @@
         }
       ]
     }
+  ],
+  "postsubmit":[
+    {
+      "name":"FrameworksVpnTests"
+    }
   ]
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 677a5d1..e2f8c50 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3194,12 +3194,7 @@
 
                     } else if (tagName.equals("verifier")) {
                         final String deviceIdentity = parser.getAttributeValue(null, "device");
-                        try {
-                            mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity);
-                        } catch (IllegalArgumentException e) {
-                            Slog.w(PackageManagerService.TAG, "Discard invalid verifier device id: "
-                                    + e.getMessage());
-                        }
+                        mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity);
                     } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
                         // No longer used.
                     } else if (tagName.equals("keyset-settings")) {
@@ -3228,7 +3223,7 @@
                 }
 
                 str.close();
-            } catch (IOException | XmlPullParserException e) {
+            } catch (IOException | XmlPullParserException | IllegalArgumentException e) {
                 // Remove corrupted file and retry.
                 atomicFile.failRead(str, e);
 
diff --git a/services/tests/VpnTests/java/android/net/Ikev2VpnProfileTest.java b/services/tests/VpnTests/java/android/net/Ikev2VpnProfileTest.java
new file mode 100644
index 0000000..e12e961f
--- /dev/null
+++ b/services/tests/VpnTests/java/android/net/Ikev2VpnProfileTest.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2019 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 android.net;
+
+import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V6;
+import static android.net.cts.util.IkeSessionTestUtils.getTestIkeSessionParams;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.os.Build;
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.net.VpnProfile;
+import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
+import com.android.net.module.util.ProxyUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.auth.x500.X500Principal;
+
+/** Unit tests for {@link Ikev2VpnProfile.Builder}. */
+@SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+public class Ikev2VpnProfileTest {
+    private static final String SERVER_ADDR_STRING = "1.2.3.4";
+    private static final String IDENTITY_STRING = "Identity";
+    private static final String USERNAME_STRING = "username";
+    private static final String PASSWORD_STRING = "pa55w0rd";
+    private static final String EXCL_LIST = "exclList";
+    private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+    private static final int TEST_MTU = 1300;
+
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
+    private final MockContext mMockContext =
+            new MockContext() {
+                @Override
+                public String getOpPackageName() {
+                    return "fooPackage";
+                }
+            };
+    private final ProxyInfo mProxy = ProxyInfo.buildDirectProxy(
+            SERVER_ADDR_STRING, -1, ProxyUtils.exclusionStringAsList(EXCL_LIST));
+
+    private X509Certificate mUserCert;
+    private X509Certificate mServerRootCa;
+    private PrivateKey mPrivateKey;
+
+    @Before
+    public void setUp() throws Exception {
+        mServerRootCa = generateRandomCertAndKeyPair().cert;
+
+        final CertificateAndKey userCertKey = generateRandomCertAndKeyPair();
+        mUserCert = userCertKey.cert;
+        mPrivateKey = userCertKey.key;
+    }
+
+    private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() {
+        final Ikev2VpnProfile.Builder builder =
+                new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING);
+
+        builder.setBypassable(true);
+        builder.setProxy(mProxy);
+        builder.setMaxMtu(TEST_MTU);
+        builder.setMetered(true);
+
+        return builder;
+    }
+
+    @Test
+    public void testBuildValidProfileWithOptions() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        // Check non-auth parameters correctly stored
+        assertEquals(SERVER_ADDR_STRING, profile.getServerAddr());
+        assertEquals(IDENTITY_STRING, profile.getUserIdentity());
+        assertEquals(mProxy, profile.getProxyInfo());
+        assertTrue(profile.isBypassable());
+        assertTrue(profile.isMetered());
+        assertEquals(TEST_MTU, profile.getMaxMtu());
+        assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms());
+    }
+
+    @Test
+    public void testBuildUsernamePasswordProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        assertEquals(USERNAME_STRING, profile.getUsername());
+        assertEquals(PASSWORD_STRING, profile.getPassword());
+        assertEquals(mServerRootCa, profile.getServerRootCaCert());
+
+        assertNull(profile.getPresharedKey());
+        assertNull(profile.getRsaPrivateKey());
+        assertNull(profile.getUserCert());
+    }
+
+    @Test
+    public void testBuildDigitalSignatureProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        assertEquals(profile.getUserCert(), mUserCert);
+        assertEquals(mPrivateKey, profile.getRsaPrivateKey());
+        assertEquals(profile.getServerRootCaCert(), mServerRootCa);
+
+        assertNull(profile.getPresharedKey());
+        assertNull(profile.getUsername());
+        assertNull(profile.getPassword());
+    }
+
+    @Test
+    public void testBuildPresharedKeyProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        assertArrayEquals(PSK_BYTES, profile.getPresharedKey());
+
+        assertNull(profile.getServerRootCaCert());
+        assertNull(profile.getUsername());
+        assertNull(profile.getPassword());
+        assertNull(profile.getRsaPrivateKey());
+        assertNull(profile.getUserCert());
+    }
+
+    @Test
+    public void testBuildWithAllowedAlgorithmsAead() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        builder.setAuthPsk(PSK_BYTES);
+
+        List<String> allowedAlgorithms =
+                Arrays.asList(
+                        IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+                        IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305);
+        builder.setAllowedAlgorithms(allowedAlgorithms);
+
+        final Ikev2VpnProfile profile = builder.build();
+        assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+    }
+
+    @Test
+    public void testBuildWithAllowedAlgorithmsNormal() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        builder.setAuthPsk(PSK_BYTES);
+
+        List<String> allowedAlgorithms =
+                Arrays.asList(
+                        IpSecAlgorithm.AUTH_HMAC_SHA512,
+                        IpSecAlgorithm.AUTH_AES_XCBC,
+                        IpSecAlgorithm.AUTH_AES_CMAC,
+                        IpSecAlgorithm.CRYPT_AES_CBC,
+                        IpSecAlgorithm.CRYPT_AES_CTR);
+        builder.setAllowedAlgorithms(allowedAlgorithms);
+
+        final Ikev2VpnProfile profile = builder.build();
+        assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+    }
+
+    @Test
+    public void testSetAllowedAlgorithmsEmptyList() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        try {
+            builder.setAllowedAlgorithms(new ArrayList<>());
+            fail("Expected exception due to no valid algorithm set");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testSetAllowedAlgorithmsInvalidList() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        List<String> allowedAlgorithms = new ArrayList<>();
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256));
+            fail("Expected exception due to missing encryption");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC));
+            fail("Expected exception due to missing authentication");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        List<String> allowedAlgorithms = new ArrayList<>();
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5));
+            fail("Expected exception due to insecure algorithm");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1));
+            fail("Expected exception due to insecure algorithm");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testBuildNoAuthMethodSet() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        try {
+            builder.build();
+            fail("Expected exception due to lack of auth method");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+
+    // TODO: Refer to Build.VERSION_CODES.SC_V2 when it's available in AOSP and mainline branch
+    @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
+    @Test
+    public void testBuildExcludeLocalRoutesSet() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        builder.setAuthPsk(PSK_BYTES);
+        builder.setLocalRoutesExcluded(true);
+
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+        assertTrue(profile.areLocalRoutesExcluded());
+
+        builder.setBypassable(false);
+        try {
+            builder.build();
+            fail("Expected exception because excludeLocalRoutes should be set only"
+                    + " on the bypassable VPN");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testBuildInvalidMtu() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        try {
+            builder.setMaxMtu(500);
+            fail("Expected exception due to too-small MTU");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    private void verifyVpnProfileCommon(VpnProfile profile) {
+        assertEquals(SERVER_ADDR_STRING, profile.server);
+        assertEquals(IDENTITY_STRING, profile.ipsecIdentifier);
+        assertEquals(mProxy, profile.proxy);
+        assertTrue(profile.isBypassable);
+        assertTrue(profile.isMetered);
+        assertEquals(TEST_MTU, profile.maxMtu);
+    }
+
+    @Test
+    public void testPskConvertToVpnProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final VpnProfile profile = builder.build().toVpnProfile();
+
+        verifyVpnProfileCommon(profile);
+        assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret);
+
+        // Check nothing else is set
+        assertEquals("", profile.username);
+        assertEquals("", profile.password);
+        assertEquals("", profile.ipsecUserCert);
+        assertEquals("", profile.ipsecCaCert);
+    }
+
+    @Test
+    public void testUsernamePasswordConvertToVpnProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+
+        verifyVpnProfileCommon(profile);
+        assertEquals(USERNAME_STRING, profile.username);
+        assertEquals(PASSWORD_STRING, profile.password);
+        assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+        // Check nothing else is set
+        assertEquals("", profile.ipsecUserCert);
+        assertEquals("", profile.ipsecSecret);
+    }
+
+    @Test
+    public void testRsaConvertToVpnProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+
+        final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE
+                + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded());
+        verifyVpnProfileCommon(profile);
+        assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
+        assertEquals(
+                expectedSecret,
+                profile.ipsecSecret);
+        assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+        // Check nothing else is set
+        assertEquals("", profile.username);
+        assertEquals("", profile.password);
+    }
+
+    @Test
+    public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final VpnProfile profile = builder.build().toVpnProfile();
+        profile.username = USERNAME_STRING;
+        profile.password = PASSWORD_STRING;
+        profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
+        profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+        final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+        assertNull(result.getUsername());
+        assertNull(result.getPassword());
+        assertNull(result.getUserCert());
+        assertNull(result.getRsaPrivateKey());
+        assertNull(result.getServerRootCaCert());
+    }
+
+    @Test
+    public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+        profile.ipsecSecret = new String(PSK_BYTES);
+        profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+        final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+        assertNull(result.getPresharedKey());
+        assertNull(result.getUserCert());
+        assertNull(result.getRsaPrivateKey());
+    }
+
+    @Test
+    public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+        profile.username = USERNAME_STRING;
+        profile.password = PASSWORD_STRING;
+
+        final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+        assertNull(result.getUsername());
+        assertNull(result.getPassword());
+        assertNull(result.getPresharedKey());
+    }
+
+    @Test
+    public void testPskConversionIsLossless() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    @Test
+    public void testUsernamePasswordConversionIsLossless() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    @Test
+    public void testRsaConversionIsLossless() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    @Test
+    public void testBuildWithIkeTunConnParamsConvertToVpnProfile() throws Exception {
+        // Special keyId that contains delimiter character of VpnProfile
+        final byte[] keyId = "foo\0bar".getBytes();
+        final IkeTunnelConnectionParams tunnelParams = new IkeTunnelConnectionParams(
+                getTestIkeSessionParams(true /* testIpv6 */, new IkeKeyIdIdentification(keyId)),
+                CHILD_PARAMS);
+        final Ikev2VpnProfile ikev2VpnProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
+        final VpnProfile vpnProfile = ikev2VpnProfile.toVpnProfile();
+
+        assertEquals(VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS, vpnProfile.type);
+
+        // Username, password, server, ipsecIdentifier, ipsecCaCert, ipsecSecret, ipsecUserCert and
+        // getAllowedAlgorithms should not be set if IkeTunnelConnectionParams is set.
+        assertEquals("", vpnProfile.server);
+        assertEquals("", vpnProfile.ipsecIdentifier);
+        assertEquals("", vpnProfile.username);
+        assertEquals("", vpnProfile.password);
+        assertEquals("", vpnProfile.ipsecCaCert);
+        assertEquals("", vpnProfile.ipsecSecret);
+        assertEquals("", vpnProfile.ipsecUserCert);
+        assertEquals(0, vpnProfile.getAllowedAlgorithms().size());
+
+        // IkeTunnelConnectionParams should stay the same.
+        assertEquals(tunnelParams, vpnProfile.ikeTunConnParams);
+
+        // Convert to disk-stable format and then back to Ikev2VpnProfile should be the same.
+        final VpnProfile decodedVpnProfile =
+                VpnProfile.decode(vpnProfile.key, vpnProfile.encode());
+        final Ikev2VpnProfile convertedIkev2VpnProfile =
+                Ikev2VpnProfile.fromVpnProfile(decodedVpnProfile);
+        assertEquals(ikev2VpnProfile, convertedIkev2VpnProfile);
+    }
+
+    @Test
+    public void testConversionIsLosslessWithIkeTunConnParams() throws Exception {
+        final IkeTunnelConnectionParams tunnelParams =
+                new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
+        // Config authentication related fields is not required while building with
+        // IkeTunnelConnectionParams.
+        final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    @Test
+    public void testAutomaticNattAndIpVersionConversionIsLossless() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        builder.setAutomaticNattKeepaliveTimerEnabled(true);
+        builder.setAutomaticIpVersionSelectionEnabled(true);
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    @Test
+    public void testAutomaticNattAndIpVersionDefaults() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(false, ikeProfile.isAutomaticNattKeepaliveTimerEnabled());
+        assertEquals(false, ikeProfile.isAutomaticIpVersionSelectionEnabled());
+    }
+
+    @Test
+    public void testEquals() throws Exception {
+        // Verify building without IkeTunnelConnectionParams
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        assertEquals(builder.build(), builder.build());
+
+        // Verify building with IkeTunnelConnectionParams
+        final IkeTunnelConnectionParams tunnelParams =
+                new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
+        final IkeTunnelConnectionParams tunnelParams2 =
+                new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
+        assertEquals(new Ikev2VpnProfile.Builder(tunnelParams).build(),
+                new Ikev2VpnProfile.Builder(tunnelParams2).build());
+    }
+
+    @Test
+    public void testBuildProfileWithNullProxy() throws Exception {
+        final Ikev2VpnProfile ikev2VpnProfile =
+                new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
+                        .setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa)
+                        .build();
+
+        // ProxyInfo should be null for the profile without setting ProxyInfo.
+        assertNull(ikev2VpnProfile.getProxyInfo());
+
+        // ProxyInfo should stay null after performing toVpnProfile() and fromVpnProfile()
+        final VpnProfile vpnProfile = ikev2VpnProfile.toVpnProfile();
+        assertNull(vpnProfile.proxy);
+
+        final Ikev2VpnProfile convertedIkev2VpnProfile = Ikev2VpnProfile.fromVpnProfile(vpnProfile);
+        assertNull(convertedIkev2VpnProfile.getProxyInfo());
+    }
+
+    private static class CertificateAndKey {
+        public final X509Certificate cert;
+        public final PrivateKey key;
+
+        CertificateAndKey(X509Certificate cert, PrivateKey key) {
+            this.cert = cert;
+            this.key = key;
+        }
+    }
+
+    private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception {
+        final Date validityBeginDate =
+                new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L));
+        final Date validityEndDate =
+                new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L));
+
+        // Generate a keypair
+        final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+        keyPairGenerator.initialize(512);
+        final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+        final X500Principal dnName = new X500Principal("CN=test.android.com");
+        final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+        certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+        certGen.setSubjectDN(dnName);
+        certGen.setIssuerDN(dnName);
+        certGen.setNotBefore(validityBeginDate);
+        certGen.setNotAfter(validityEndDate);
+        certGen.setPublicKey(keyPair.getPublic());
+        certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+        final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL");
+        return new CertificateAndKey(cert, keyPair.getPrivate());
+    }
+}
diff --git a/services/tests/VpnTests/java/android/net/VpnManagerTest.java b/services/tests/VpnTests/java/android/net/VpnManagerTest.java
new file mode 100644
index 0000000..365b4d1
--- /dev/null
+++ b/services/tests/VpnTests/java/android/net/VpnManagerTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 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 android.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.test.mock.MockContext;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.MessageUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link VpnManager}. */
+@SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+public class VpnManagerTest {
+
+    private static final String PKG_NAME = "fooPackage";
+
+    private static final String SESSION_NAME_STRING = "testSession";
+    private static final String SERVER_ADDR_STRING = "1.2.3.4";
+    private static final String IDENTITY_STRING = "Identity";
+    private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+
+    private IVpnManager mMockService;
+    private VpnManager mVpnManager;
+    private final MockContext mMockContext =
+            new MockContext() {
+                @Override
+                public String getOpPackageName() {
+                    return PKG_NAME;
+                }
+            };
+
+    @Before
+    public void setUp() throws Exception {
+        assumeFalse("Skipping test because watches don't support VPN",
+                InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WATCH));
+        mMockService = mock(IVpnManager.class);
+        mVpnManager = new VpnManager(mMockContext, mMockService);
+    }
+
+    @Test
+    public void testProvisionVpnProfilePreconsented() throws Exception {
+        final PlatformVpnProfile profile = getPlatformVpnProfile();
+        when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
+                .thenReturn(true);
+
+        // Expect there to be no intent returned, as consent has already been granted.
+        assertNull(mVpnManager.provisionVpnProfile(profile));
+        verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+    }
+
+    @Test
+    public void testProvisionVpnProfileNeedsConsent() throws Exception {
+        final PlatformVpnProfile profile = getPlatformVpnProfile();
+        when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
+                .thenReturn(false);
+
+        // Expect intent to be returned, as consent has not already been granted.
+        final Intent intent = mVpnManager.provisionVpnProfile(profile);
+        assertNotNull(intent);
+
+        final ComponentName expectedComponentName =
+                ComponentName.unflattenFromString(
+                        "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
+        assertEquals(expectedComponentName, intent.getComponent());
+        verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+    }
+
+    @Test
+    public void testDeleteProvisionedVpnProfile() throws Exception {
+        mVpnManager.deleteProvisionedVpnProfile();
+        verify(mMockService).deleteVpnProfile(eq(PKG_NAME));
+    }
+
+    @Test
+    public void testStartProvisionedVpnProfile() throws Exception {
+        mVpnManager.startProvisionedVpnProfile();
+        verify(mMockService).startVpnProfile(eq(PKG_NAME));
+    }
+
+    @Test
+    public void testStopProvisionedVpnProfile() throws Exception {
+        mVpnManager.stopProvisionedVpnProfile();
+        verify(mMockService).stopVpnProfile(eq(PKG_NAME));
+    }
+
+    private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
+        return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
+                .setBypassable(true)
+                .setMaxMtu(1300)
+                .setMetered(true)
+                .setAuthPsk(PSK_BYTES)
+                .build();
+    }
+
+    @Test
+    public void testVpnTypesEqual() throws Exception {
+        SparseArray<String> vmVpnTypes = MessageUtils.findMessageNames(
+                new Class[] { VpnManager.class }, new String[]{ "TYPE_VPN_" });
+        SparseArray<String> nativeVpnType = MessageUtils.findMessageNames(
+                new Class[] { NativeVpnType.class }, new String[]{ "" });
+
+        // TYPE_VPN_NONE = -1 is only defined in VpnManager.
+        assertEquals(vmVpnTypes.size() - 1, nativeVpnType.size());
+        for (int i = VpnManager.TYPE_VPN_SERVICE; i < vmVpnTypes.size(); i++) {
+            assertEquals(vmVpnTypes.get(i), "TYPE_VPN_" + nativeVpnType.get(i));
+        }
+    }
+}
diff --git a/services/tests/VpnTests/java/com/android/internal/net/VpnProfileTest.java b/services/tests/VpnTests/java/com/android/internal/net/VpnProfileTest.java
new file mode 100644
index 0000000..acae7d2
--- /dev/null
+++ b/services/tests/VpnTests/java/com/android/internal/net/VpnProfileTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V4;
+
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.IpSecAlgorithm;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** Unit tests for {@link VpnProfile}. */
+@SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+public class VpnProfileTest {
+    private static final String DUMMY_PROFILE_KEY = "Test";
+
+    private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
+    private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
+    private static final int ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE = 25;
+    private static final int ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION = 26;
+    private static final int ENCODED_INDEX_IKE_TUN_CONN_PARAMS = 27;
+    private static final int ENCODED_INDEX_AUTOMATIC_NATT_KEEPALIVE_TIMER_ENABLED = 28;
+    private static final int ENCODED_INDEX_AUTOMATIC_IP_VERSION_SELECTION_ENABLED = 29;
+
+    @Test
+    public void testDefaults() throws Exception {
+        final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
+
+        assertEquals(DUMMY_PROFILE_KEY, p.key);
+        assertEquals("", p.name);
+        assertEquals(VpnProfile.TYPE_PPTP, p.type);
+        assertEquals("", p.server);
+        assertEquals("", p.username);
+        assertEquals("", p.password);
+        assertEquals("", p.dnsServers);
+        assertEquals("", p.searchDomains);
+        assertEquals("", p.routes);
+        assertTrue(p.mppe);
+        assertEquals("", p.l2tpSecret);
+        assertEquals("", p.ipsecIdentifier);
+        assertEquals("", p.ipsecSecret);
+        assertEquals("", p.ipsecUserCert);
+        assertEquals("", p.ipsecCaCert);
+        assertEquals("", p.ipsecServerCert);
+        assertEquals(null, p.proxy);
+        assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
+        assertFalse(p.isBypassable);
+        assertFalse(p.isMetered);
+        assertEquals(1360, p.maxMtu);
+        assertFalse(p.areAuthParamsInline);
+        assertFalse(p.isRestrictedToTestNetworks);
+        assertFalse(p.excludeLocalRoutes);
+        assertFalse(p.requiresInternetValidation);
+        assertFalse(p.automaticNattKeepaliveTimerEnabled);
+        assertFalse(p.automaticIpVersionSelectionEnabled);
+    }
+
+    private VpnProfile getSampleIkev2Profile(String key) {
+        final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
+                false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
+                null /* ikeTunConnParams */, true /* mAutomaticNattKeepaliveTimerEnabled */,
+                true /* automaticIpVersionSelectionEnabled */);
+
+        p.name = "foo";
+        p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+        p.server = "bar";
+        p.username = "baz";
+        p.password = "qux";
+        p.dnsServers = "8.8.8.8";
+        p.searchDomains = "";
+        p.routes = "0.0.0.0/0";
+        p.mppe = false;
+        p.l2tpSecret = "";
+        p.ipsecIdentifier = "quux";
+        p.ipsecSecret = "quuz";
+        p.ipsecUserCert = "corge";
+        p.ipsecCaCert = "grault";
+        p.ipsecServerCert = "garply";
+        p.proxy = null;
+        p.setAllowedAlgorithms(
+                Arrays.asList(
+                        IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+                        IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
+                        IpSecAlgorithm.AUTH_HMAC_SHA512,
+                        IpSecAlgorithm.CRYPT_AES_CBC));
+        p.isBypassable = true;
+        p.isMetered = true;
+        p.maxMtu = 1350;
+        p.areAuthParamsInline = true;
+
+        // Not saved, but also not compared.
+        p.saveLogin = true;
+
+        return p;
+    }
+
+    private VpnProfile getSampleIkev2ProfileWithIkeTunConnParams(String key) {
+        final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
+                false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
+                new IkeTunnelConnectionParams(IKE_PARAMS_V4, CHILD_PARAMS),
+                true /* mAutomaticNattKeepaliveTimerEnabled */,
+                true /* automaticIpVersionSelectionEnabled */);
+
+        p.name = "foo";
+        p.server = "bar";
+        p.dnsServers = "8.8.8.8";
+        p.searchDomains = "";
+        p.routes = "0.0.0.0/0";
+        p.mppe = false;
+        p.proxy = null;
+        p.setAllowedAlgorithms(
+                Arrays.asList(
+                        IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+                        IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
+                        IpSecAlgorithm.AUTH_HMAC_SHA512,
+                        IpSecAlgorithm.CRYPT_AES_CBC));
+        p.isBypassable = true;
+        p.isMetered = true;
+        p.maxMtu = 1350;
+        p.areAuthParamsInline = true;
+
+        // Not saved, but also not compared.
+        p.saveLogin = true;
+
+        return p;
+    }
+
+    @Test
+    public void testEquals() {
+        assertEquals(
+                getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY));
+
+        final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        modified.maxMtu--;
+        assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified);
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        if (isAtLeastU()) {
+            // automaticNattKeepaliveTimerEnabled, automaticIpVersionSelectionEnabled added in U.
+            assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 28);
+            assertParcelSane(getSampleIkev2ProfileWithIkeTunConnParams(DUMMY_PROFILE_KEY), 28);
+        } else if (isAtLeastT()) {
+            // excludeLocalRoutes, requiresPlatformValidation were added in T.
+            assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 26);
+            assertParcelSane(getSampleIkev2ProfileWithIkeTunConnParams(DUMMY_PROFILE_KEY), 26);
+        } else {
+            assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
+        }
+    }
+
+    @Test
+    public void testEncodeDecodeWithIkeTunConnParams() {
+        final VpnProfile profile = getSampleIkev2ProfileWithIkeTunConnParams(DUMMY_PROFILE_KEY);
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+        assertEquals(profile, decoded);
+    }
+
+    @Test
+    public void testEncodeDecode() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+        assertEquals(profile, decoded);
+    }
+
+    @Test
+    public void testEncodeDecodeTooManyValues() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        final byte[] tooManyValues =
+                (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes();
+
+        assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
+    }
+
+    private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
+        // Sort to ensure when we remove, we can do it from greatest first.
+        Arrays.sort(missingIndices);
+
+        final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
+        final List<String> parts =
+                new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
+
+        // Remove from back first to ensure indexing is consistent.
+        for (int i = missingIndices.length - 1; i >= 0; i--) {
+            parts.remove(missingIndices[i]);
+        }
+
+        return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
+    }
+
+    @Test
+    public void testEncodeDecodeInvalidNumberOfValues() {
+        final String tooFewValues =
+                getEncodedDecodedIkev2ProfileMissingValues(
+                        ENCODED_INDEX_AUTH_PARAMS_INLINE,
+                        ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS,
+                        ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE,
+                        ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION,
+                        ENCODED_INDEX_IKE_TUN_CONN_PARAMS,
+                        ENCODED_INDEX_AUTOMATIC_NATT_KEEPALIVE_TIMER_ENABLED,
+                        ENCODED_INDEX_AUTOMATIC_IP_VERSION_SELECTION_ENABLED
+                        /* missingIndices */);
+
+        assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
+    }
+
+    private String getEncodedDecodedIkev2ProfileWithtooFewValues() {
+        return getEncodedDecodedIkev2ProfileMissingValues(
+                ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS,
+                ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE,
+                ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION,
+                ENCODED_INDEX_IKE_TUN_CONN_PARAMS,
+                ENCODED_INDEX_AUTOMATIC_NATT_KEEPALIVE_TIMER_ENABLED,
+                ENCODED_INDEX_AUTOMATIC_IP_VERSION_SELECTION_ENABLED /* missingIndices */);
+    }
+
+    @Test
+    public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
+        final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+        // Verify decoding without isRestrictedToTestNetworks defaults to false
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+        assertFalse(decoded.isRestrictedToTestNetworks);
+    }
+
+    @Test
+    public void testEncodeDecodeMissingExcludeLocalRoutes() {
+        final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+        // Verify decoding without excludeLocalRoutes defaults to false
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+        assertFalse(decoded.excludeLocalRoutes);
+    }
+
+    @Test
+    public void testEncodeDecodeMissingRequiresValidation() {
+        final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+        // Verify decoding without requiresValidation defaults to false
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+        assertFalse(decoded.requiresInternetValidation);
+    }
+
+    @Test
+    public void testEncodeDecodeMissingAutomaticNattKeepaliveTimerEnabled() {
+        final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+        // Verify decoding without automaticNattKeepaliveTimerEnabled defaults to false
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+        assertFalse(decoded.automaticNattKeepaliveTimerEnabled);
+    }
+
+    @Test
+    public void testEncodeDecodeMissingAutomaticIpVersionSelectionEnabled() {
+        final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+        // Verify decoding without automaticIpVersionSelectionEnabled defaults to false
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+        assertFalse(decoded.automaticIpVersionSelectionEnabled);
+    }
+
+    @Test
+    public void testEncodeDecodeLoginsNotSaved() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        profile.saveLogin = false;
+
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+        assertNotEquals(profile, decoded);
+
+        // Add the username/password back, everything else must be equal.
+        decoded.username = profile.username;
+        decoded.password = profile.password;
+        assertEquals(profile, decoded);
+    }
+
+    @Test
+    public void testClone() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        final VpnProfile clone = profile.clone();
+        assertEquals(profile, clone);
+        assertNotSame(profile, clone);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java b/services/tests/VpnTests/java/com/android/server/net/LockdownVpnTrackerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
rename to services/tests/VpnTests/java/com/android/server/net/LockdownVpnTrackerTest.java
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
new file mode 100644
index 0000000..e94b8ad
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -0,0 +1,56 @@
+// Copyright (C) 2024 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "RollbackPackageHealthObserverTests",
+
+    srcs: [
+        "*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.runner",
+        "mockito-target-extended-minus-junit4",
+        "services.core",
+        "truth",
+        "flag-junit",
+    ],
+
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: [
+        "device-tests",
+        "automotive-tests",
+    ],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml
new file mode 100644
index 0000000..c52dbde
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.server.rollback">
+
+    <uses-sdk android:targetSdkVersion="35" />
+
+    <application android:testOnly="true"
+                 android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.server.rollback"
+        android:label="Frameworks Rollback Package Health Observer test" />
+</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml
new file mode 100644
index 0000000..635183c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+<configuration description="Runs Rollback Package Health Observer Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="RollbackPackageHealthObserverTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="RollbackPackageHealthObserverTests" />
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.server.rollback" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
index e42bdad..6ac56bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   "postsubmit": [
     {
-      "name": "FrameworksMockingServicesTests",
+      "name": "RollbackPackageHealthObserverTests",
       "options": [
         {
           "include-filter": "com.android.server.rollback"
diff --git a/tests/graphics/HwAccelerationTest/jni/native-lib.cpp b/tests/graphics/HwAccelerationTest/jni/native-lib.cpp
index 407d4bf..2977c21 100644
--- a/tests/graphics/HwAccelerationTest/jni/native-lib.cpp
+++ b/tests/graphics/HwAccelerationTest/jni/native-lib.cpp
@@ -30,7 +30,7 @@
 
     void setBuffer(AHardwareBuffer* buffer) {
         ASurfaceTransaction* transaction = ASurfaceTransaction_create();
-        ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer);
+        ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer, -1);
         ASurfaceTransaction_setVisibility(transaction, surfaceControl,
                                           ASURFACE_TRANSACTION_VISIBILITY_SHOW);
         ASurfaceTransaction_apply(transaction);