[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 955155ce65 -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/24616903

Change-Id: I9a8d61718c90096f61fc0f652172b8fcaa966e1a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index d3b01ea..bb3dc24 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -110,7 +110,6 @@
     ],
     apps: [
         "ServiceConnectivityResources",
-        "HalfSheetUX",
     ],
     prebuilts: ["current_sdkinfo"],
     manifest: "manifest.json",
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index bb09d0d..eadba58 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -283,6 +283,7 @@
     private List<TetheredClient> mDhcpLeases = Collections.emptyList();
 
     private int mLastIPv6UpstreamIfindex = 0;
+    private boolean mUpstreamSupportsBpf = false;
 
     private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
         public void accept(NeighborEvent e) {
@@ -779,15 +780,15 @@
 
         // If v6only is null, we pass in null to setRaParams(), which handles
         // deprecation of any existing RA data.
-
         setRaParams(params);
-        // Be aware that updateIpv6ForwardingRules use mLastIPv6LinkProperties, so this line should
-        // be eariler than updateIpv6ForwardingRules.
-        // TODO: avoid this dependencies and move this logic into BpfCoordinator.
-        mLastIPv6LinkProperties = v6only;
 
-        updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null);
+        // Not support BPF on virtual upstream interface
+        final boolean upstreamSupportsBpf = upstreamIface != null && !isVcnInterface(upstreamIface);
+        updateIpv6ForwardingRules(
+                mLastIPv6UpstreamIfindex, upstreamIfIndex, upstreamSupportsBpf, null);
+        mLastIPv6LinkProperties = v6only;
         mLastIPv6UpstreamIfindex = upstreamIfIndex;
+        mUpstreamSupportsBpf = upstreamSupportsBpf;
         if (mDadProxy != null) {
             mDadProxy.setUpstreamIface(upstreamIfaceParams);
         }
@@ -921,20 +922,14 @@
         mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex);
     }
 
-    private boolean isIpv6VcnNetworkInterface() {
-        if (mLastIPv6LinkProperties == null) return false;
-
-        return isVcnInterface(mLastIPv6LinkProperties.getInterfaceName());
-    }
-
     // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
     // changes or if a neighbor event is received.
     private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
-            NeighborEvent e) {
-        // If no longer have an upstream or it is virtual network, clear forwarding rules and do
+            boolean upstreamSupportsBpf, NeighborEvent e) {
+        // If no longer have an upstream or upstream not supports BPF, clear forwarding rules and do
         // nothing else.
         // TODO: Rather than always clear rules, ensure whether ipv6 ever enable first.
-        if (upstreamIfindex == 0 || isIpv6VcnNetworkInterface()) {
+        if (upstreamIfindex == 0 || !upstreamSupportsBpf) {
             clearIpv6ForwardingRules();
             return;
         }
@@ -995,7 +990,8 @@
         if (mInterfaceParams != null
                 && mInterfaceParams.index == e.ifindex
                 && mInterfaceParams.hasMacAddress) {
-            updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
+            updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex,
+                    mUpstreamSupportsBpf, e);
             updateClientInfoIpv4(e);
         }
     }
diff --git a/framework-t/api/module-lib-current.txt b/framework-t/api/module-lib-current.txt
index 42c83d8..5a8d47b 100644
--- a/framework-t/api/module-lib-current.txt
+++ b/framework-t/api/module-lib-current.txt
@@ -207,43 +207,3 @@
 
 }
 
-package android.remoteauth {
-
-  public interface DeviceDiscoveryCallback {
-    method public void onDeviceUpdate(@NonNull android.remoteauth.RemoteDevice, int);
-    method public void onTimeout();
-    field public static final int STATE_LOST = 0; // 0x0
-    field public static final int STATE_SEEN = 1; // 0x1
-  }
-
-  public final class RemoteAuthFrameworkInitializer {
-    method public static void registerServiceWrappers();
-  }
-
-  public class RemoteAuthManager {
-    method public boolean isRemoteAuthSupported();
-    method public boolean startDiscovery(int, @NonNull java.util.concurrent.Executor, @NonNull android.remoteauth.DeviceDiscoveryCallback);
-    method public void stopDiscovery(@NonNull android.remoteauth.DeviceDiscoveryCallback);
-  }
-
-  public final class RemoteDevice implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public int getConnectionId();
-    method @Nullable public String getName();
-    method public int getRegistrationState();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.remoteauth.RemoteDevice> CREATOR;
-    field public static final int STATE_NOT_REGISTERED = 0; // 0x0
-    field public static final int STATE_REGISTERED = 1; // 0x1
-  }
-
-  public static final class RemoteDevice.Builder {
-    ctor public RemoteDevice.Builder(int);
-    method @NonNull public android.remoteauth.RemoteDevice build();
-    method @NonNull public android.remoteauth.RemoteDevice.Builder setConnectionId(int);
-    method @NonNull public android.remoteauth.RemoteDevice.Builder setName(@Nullable String);
-    method @NonNull public android.remoteauth.RemoteDevice.Builder setRegistrationState(int);
-  }
-
-}
-
diff --git a/framework/Android.bp b/framework/Android.bp
index e663764..e577e6d 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -80,6 +80,7 @@
     impl_only_libs: [
         // TODO: figure out why just using "framework-tethering" uses the stubs, even though both
         // framework-connectivity and framework-tethering are in the same APEX.
+        "framework-location.stubs.module_lib",
         "framework-tethering.impl",
         "framework-wifi.stubs.module_lib",
     ],
@@ -127,6 +128,7 @@
         // to generate the connectivity stubs. That would create a circular dependency
         // because the tethering impl depend on the connectivity stubs (e.g.,
         // TetheringRequest depends on LinkAddress).
+        "framework-location.stubs.module_lib",
         "framework-tethering.impl",
         "framework-wifi.stubs.module_lib",
     ],
diff --git a/nearby/framework/java/android/nearby/PairStatusMetadata.java b/nearby/framework/java/android/nearby/PairStatusMetadata.java
index 438cd6b..a4cd134 100644
--- a/nearby/framework/java/android/nearby/PairStatusMetadata.java
+++ b/nearby/framework/java/android/nearby/PairStatusMetadata.java
@@ -106,11 +106,6 @@
     }
 
     @Override
-    public int getStability() {
-        return 0;
-    }
-
-    @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mStatus);
     }
diff --git a/nearby/halfsheet/res/values-cs/strings.xml b/nearby/halfsheet/res/values-cs/strings.xml
index 872eef5..53d27ab 100644
--- a/nearby/halfsheet/res/values-cs/strings.xml
+++ b/nearby/halfsheet/res/values-cs/strings.xml
@@ -19,7 +19,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="fast_pair_setup_in_progress" msgid="4158762239172829807">"Zahajování nastavení…"</string>
     <string name="fast_pair_title_setup" msgid="2894360355540593246">"Nastavení zařízení"</string>
-    <string name="fast_pair_device_ready" msgid="2903490346082833101">"Zařízení připojeno"</string>
+    <string name="fast_pair_device_ready" msgid="2903490346082833101">"Zařízení je připojeno"</string>
     <string name="fast_pair_device_ready_with_device_name" msgid="2151967995692339422">"Připojeno k zařízení %s"</string>
     <string name="fast_pair_title_fail" msgid="5677174346601290232">"Nelze se připojit"</string>
     <string name="fast_pair_unable_to_connect" msgid="3661854812014294569">"Nepodařilo se připojit"</string>
diff --git a/nearby/halfsheet/res/values-sk/strings.xml b/nearby/halfsheet/res/values-sk/strings.xml
index 7938211..f7ab21f 100644
--- a/nearby/halfsheet/res/values-sk/strings.xml
+++ b/nearby/halfsheet/res/values-sk/strings.xml
@@ -19,7 +19,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="fast_pair_setup_in_progress" msgid="4158762239172829807">"Spúšťa sa nastavenie…"</string>
     <string name="fast_pair_title_setup" msgid="2894360355540593246">"Nastavte zariadenie"</string>
-    <string name="fast_pair_device_ready" msgid="2903490346082833101">"Zariadenie je pripojené"</string>
+    <string name="fast_pair_device_ready" msgid="2903490346082833101">"Zariadenie bolo pripojené"</string>
     <string name="fast_pair_device_ready_with_device_name" msgid="2151967995692339422">"Pripojené k zariadeniu %s"</string>
     <string name="fast_pair_title_fail" msgid="5677174346601290232">"Nepodarilo sa pripojiť"</string>
     <string name="fast_pair_unable_to_connect" msgid="3661854812014294569">"Nepodarilo sa pripojiť"</string>
diff --git a/nearby/halfsheet/res/values-zh-rCN/strings.xml b/nearby/halfsheet/res/values-zh-rCN/strings.xml
index 55dd8b0..8117bac 100644
--- a/nearby/halfsheet/res/values-zh-rCN/strings.xml
+++ b/nearby/halfsheet/res/values-zh-rCN/strings.xml
@@ -26,7 +26,7 @@
     <string name="fast_pair_unable_to_connect_description" msgid="3926830740860653891">"请尝试手动与该设备配对"</string>
     <string name="fast_pair_turn_on_bt_device_pairing_mode" msgid="3197372738187738030">"请尝试让设备进入配对模式"</string>
     <string name="devices_within_reach_channel_name" msgid="876280551450910440">"附近的设备"</string>
-    <string name="devices_with_your_account_channel_name" msgid="8120067812798598102">"与您的账号相关联的设备"</string>
+    <string name="devices_with_your_account_channel_name" msgid="8120067812798598102">"与您的帐号相关联的设备"</string>
     <string name="fast_pair_your_device" msgid="3662423897069320840">"您保存的设备已可供使用"</string>
     <string name="common_nearby_title" msgid="5480324514713607015">"附近"</string>
     <string name="common_devices" msgid="2635603125608104442">"设备"</string>
diff --git a/nearby/halfsheet/res/values-zh-rHK/strings.xml b/nearby/halfsheet/res/values-zh-rHK/strings.xml
index 4dac931..d934f88 100644
--- a/nearby/halfsheet/res/values-zh-rHK/strings.xml
+++ b/nearby/halfsheet/res/values-zh-rHK/strings.xml
@@ -26,8 +26,8 @@
     <string name="fast_pair_unable_to_connect_description" msgid="3926830740860653891">"嘗試手動配對裝置"</string>
     <string name="fast_pair_turn_on_bt_device_pairing_mode" msgid="3197372738187738030">"嘗試讓裝置進入配對模式"</string>
     <string name="devices_within_reach_channel_name" msgid="876280551450910440">"附近的裝置"</string>
-    <string name="devices_with_your_account_channel_name" msgid="8120067812798598102">"已連結你帳戶的裝置"</string>
-    <string name="fast_pair_your_device" msgid="3662423897069320840">"你儲存的裝置已可使用"</string>
+    <string name="devices_with_your_account_channel_name" msgid="8120067812798598102">"已連結您帳戶的裝置"</string>
+    <string name="fast_pair_your_device" msgid="3662423897069320840">"您儲存的裝置已可使用"</string>
     <string name="common_nearby_title" msgid="5480324514713607015">"咫尺共享"</string>
     <string name="common_devices" msgid="2635603125608104442">"裝置"</string>
     <string name="common_connecting" msgid="160531481424245303">"正在連接…"</string>
diff --git a/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java b/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
index f53e2dc..1414f7e 100644
--- a/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
+++ b/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
@@ -16,10 +16,7 @@
 
 package android.remoteauth;
 
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 
 import androidx.annotation.IntDef;
 
@@ -31,7 +28,7 @@
  *
  * @hide
  */
-@SystemApi(client = MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
 public interface DeviceDiscoveryCallback {
     /** The device is no longer seen in the discovery process. */
     int STATE_LOST = 0;
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java b/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
index dfd7726..112ffa8 100644
--- a/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
+++ b/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
@@ -16,7 +16,6 @@
 
 package android.remoteauth;
 
-import android.annotation.SystemApi;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
 
@@ -25,7 +24,7 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
 public final class RemoteAuthFrameworkInitializer {
     private RemoteAuthFrameworkInitializer() {}
 
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java b/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
index c025a55..038af2a 100644
--- a/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
+++ b/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
@@ -16,14 +16,12 @@
 
 package android.remoteauth;
 
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.remoteauth.DeviceDiscoveryCallback.STATE_LOST;
 import static android.remoteauth.DeviceDiscoveryCallback.STATE_SEEN;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UserIdInt;
 import android.content.Context;
@@ -47,7 +45,7 @@
  *
  * @hide
  */
-@SystemApi(client = MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
 // TODO(b/290092977): Change to Context.REMOTE_AUTH_SERVICE after aosp/2681375
 // is automerges from aosp-main to udc-mainline-prod
 @SystemService(RemoteAuthManager.REMOTE_AUTH_SERVICE)
@@ -79,7 +77,7 @@
      * @return true if this device can be enrolled
      * @hide
      */
-    @SystemApi(client = MODULE_LIBRARIES)
+    // TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
     // TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
     public boolean isRemoteAuthSupported() {
         try {
@@ -100,7 +98,7 @@
      * @return {@code true} if discovery began successfully, {@code false} otherwise
      * @hide
      */
-    @SystemApi(client = MODULE_LIBRARIES)
+    // TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
     // TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
     public boolean startDiscovery(
             int timeoutMs,
@@ -149,7 +147,7 @@
     // Suppressed lint: Registration methods should have overload that accepts delivery Executor.
     // Already have executor in startDiscovery() method.
     @SuppressLint("ExecutorRegistration")
-    @SystemApi(client = MODULE_LIBRARIES)
+    // TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
     // TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
     public void stopDiscovery(@NonNull DeviceDiscoveryCallback callback) {
         Preconditions.checkNotNull(callback, "invalid null scanCallback");
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteDevice.java b/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
index 4cd2399..b6ede2e 100644
--- a/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
+++ b/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
@@ -16,12 +16,9 @@
 
 package android.remoteauth;
 
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -35,7 +32,7 @@
  * @hide
  */
 // TODO(b/295407748) Change to use @DataClass
-@SystemApi(client = MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
 public final class RemoteDevice implements Parcelable {
     /** The remote device is not registered as remote authenticator. */
     public static final int STATE_NOT_REGISTERED = 0;
diff --git a/remoteauth/service/java/com/android/server/remoteauth/jni/INativeRemoteAuthService.java b/remoteauth/service/java/com/android/server/remoteauth/jni/INativeRemoteAuthService.java
new file mode 100644
index 0000000..f79ec7e
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/jni/INativeRemoteAuthService.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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.server.remoteauth.jni;
+
+/**
+ * Interface defining a proxy between Rust and Java implementation of RemoteAuth protocol.
+ *
+ * @hide
+ */
+public interface INativeRemoteAuthService {
+    /**
+     * Interface for RemoteAuth PAL
+     *
+     * @hide
+     */
+    interface IPlatform {
+        /**
+         * Sends message to the remote authenticator
+         *
+         * @param connectionId connection ID of the {@link android.remoteauth.RemoteAuthenticator}
+         * @param request payload of the request
+         * @param callback to be used to pass the response result
+         *
+         * @hide
+         */
+        void sendRequest(int connectionId, byte[] request, ResponseCallback callback);
+
+        /**
+         * Interface for a callback to send a response back.
+         *
+         * @hide
+         */
+        interface ResponseCallback {
+            /**
+             * Invoked when message sending succeeds.
+             *
+             * @param response contains response
+             *
+             * @hide
+             */
+            void onSuccess(byte[] response);
+
+            /**
+             * Invoked when message sending fails.
+             *
+             * @param errorCode indicating the error
+             *
+             * @hide
+             */
+            void onFailure(int errorCode);
+        }
+    }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/jni/NativeRemoteAuthService.java b/remoteauth/service/java/com/android/server/remoteauth/jni/NativeRemoteAuthService.java
new file mode 100644
index 0000000..39c2a74
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/jni/NativeRemoteAuthService.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 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.server.remoteauth.jni;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.remoteauth.jni.INativeRemoteAuthService.IPlatform;
+
+/**
+ * A service providing a proxy between Rust implementation and {@link
+ * com.android.server.remoteauth.RemoteAuthService}.
+ *
+ * @hide
+ */
+public class NativeRemoteAuthService {
+    private static final String TAG = NativeRemoteAuthService.class.getSimpleName();
+
+    private IPlatform mPlatform;
+    public final Object mNativeLock = new Object();
+
+    // Constructor should receive pointers to:
+    // ConnectivityManager, RangingManager and DB
+    public NativeRemoteAuthService() {
+        System.loadLibrary("remoteauth_jni_rust");
+        synchronized (mNativeLock) {
+            native_init();
+        }
+    }
+
+    public void setDeviceListener(final IPlatform platform) {
+        mPlatform = platform;
+    }
+
+    /**
+     * Sends message to the remote authenticator
+     *
+     * @param connectionId connection ID of the {@link android.remoteauth.RemoteAuthenticator}
+     * @param request payload of the request
+     * @param responseHandle a handle associated with the request, used to pass the response to the
+     *     platform
+     * @param platformHandle a handle associated with the platform object, used to pass the response
+     *     to the specific platform
+     *
+     * @hide
+     */
+    @Keep
+    public void sendRequest(
+            int connectionId, byte[] request, long responseHandle, long platformHandle) {
+        mPlatform.sendRequest(
+                connectionId,
+                request,
+                new IPlatform.ResponseCallback() {
+                    @Override
+                    public void onSuccess(byte[] response) {
+                        synchronized (mNativeLock) {
+                            native_on_send_request_success(
+                                    response, platformHandle, responseHandle);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(int errorCode) {
+                        synchronized (mNativeLock) {
+                            native_on_send_request_error(errorCode, platformHandle, responseHandle);
+                        }
+                    }
+                });
+    }
+
+    /* Native functions implemented in JNI */
+    // This function should be implemented in remoteauth_jni_android_protocol
+    private native boolean native_init();
+
+    private native void native_on_send_request_success(
+            byte[] appResponse, long platformHandle, long responseHandle);
+
+    private native void native_on_send_request_error(
+            int errorCode, long platformHandle, long responseHandle);
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/jni/PlatformBadHandleException.java b/remoteauth/service/java/com/android/server/remoteauth/jni/PlatformBadHandleException.java
new file mode 100644
index 0000000..3ae9838
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/jni/PlatformBadHandleException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+/**
+ * Represents an unrecoverable error (invalid handle) that has occurred during accessing the
+ * platform.
+ */
+package com.android.server.remoteauth.jni;
+
+import com.android.internal.annotations.Keep;
+/**
+ * Exception thrown by native platform rust implementation of {@link
+ * com.android.server.remoteauth.RemoteAuthService}.
+ *
+ * @hide
+ */
+@Keep
+public class PlatformBadHandleException extends Exception {
+    public PlatformBadHandleException(final String message) {
+        super(message);
+    }
+
+    public PlatformBadHandleException(final Exception e) {
+        super(e);
+    }
+
+    public PlatformBadHandleException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/remoteauth/service/jni/src/lib.rs b/remoteauth/service/jni/src/lib.rs
index 0c18679..a816c94 100644
--- a/remoteauth/service/jni/src/lib.rs
+++ b/remoteauth/service/jni/src/lib.rs
@@ -19,6 +19,7 @@
 
 mod jnames;
 mod unique_jvm;
+mod utils;
 
-//pub mod remoteauth_jni_android_protocol;
 pub mod remoteauth_jni_android_platform;
+pub mod remoteauth_jni_android_protocol;
diff --git a/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs b/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs
index 4597561..f3cf3ea 100644
--- a/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs
+++ b/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs
@@ -97,12 +97,9 @@
 
 impl JavaPlatform {
     // Method to create JavaPlatform
-    pub async fn create<'a>(
-        env: JNIEnv<'a>,
-        java_platform_native: JObject<'a>,
+    pub async fn create(
+        java_platform_native: JObject<'_>,
     ) -> Result<Arc<Mutex<impl Platform>>, JNIError> {
-        let jvm = env.get_java_vm()?;
-        let _ = unique_jvm::set_once(jvm);
         let platform_handle = generate_platform_handle();
         let platform = Arc::new(Mutex::new(JavaPlatform::new(
             platform_handle,
diff --git a/remoteauth/service/jni/src/remoteauth_jni_android_protocol.rs b/remoteauth/service/jni/src/remoteauth_jni_android_protocol.rs
new file mode 100644
index 0000000..1f73207
--- /dev/null
+++ b/remoteauth/service/jni/src/remoteauth_jni_android_protocol.rs
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+use crate::unique_jvm;
+use crate::utils::get_boolean_result;
+use jni::objects::JObject;
+use jni::sys::jboolean;
+use jni::JNIEnv;
+
+#[no_mangle]
+pub extern "system" fn Java_com_android_server_remoteauth_jni_NativeRemoteAuthJavaPlatform_native_init(
+    env: JNIEnv,
+    _: JObject,
+) -> jboolean {
+    logger::init(
+        logger::Config::default()
+            .with_tag_on_device("remoteauth")
+            .with_min_level(log::Level::Trace)
+            .with_filter("trace,jni=info"),
+    );
+    get_boolean_result(native_init(env), "native_init")
+}
+
+fn native_init(env: JNIEnv) -> anyhow::Result<()> {
+    let jvm = env.get_java_vm()?;
+    unique_jvm::set_once(jvm)
+}
diff --git a/remoteauth/service/jni/src/utils.rs b/remoteauth/service/jni/src/utils.rs
new file mode 100644
index 0000000..e61b895
--- /dev/null
+++ b/remoteauth/service/jni/src/utils.rs
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+use jni::sys::jboolean;
+use log::error;
+
+pub(crate) fn get_boolean_result<T>(result: anyhow::Result<T>, error_msg: &str) -> jboolean {
+    match result {
+        Ok(_) => true,
+        Err(e) => {
+            error!("{} failed with {:?}", error_msg, &e);
+            false
+        }
+    }
+    .into()
+}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 9b99b81..f8e3166 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -424,7 +424,6 @@
 import com.android.testutils.FunctionalUtils.ThrowingRunnable;
 import com.android.testutils.HandlerUtils;
 import com.android.testutils.RecorderCallback.CallbackEntry;
-import com.android.testutils.SkipPresubmit;
 import com.android.testutils.TestableNetworkCallback;
 import com.android.testutils.TestableNetworkOfferCallback;
 
@@ -7430,7 +7429,6 @@
         assertPinnedToWifiWithCellDefault();
     }
 
-    @SkipPresubmit(reason = "Out of SLO flakiness")
     @Test
     public void testNetworkCallbackMaximum() throws Exception {
         final int MAX_REQUESTS = 100;
@@ -7549,6 +7547,19 @@
             NetworkCallback networkCallback = new NetworkCallback();
             mCm.requestNetwork(networkRequest, networkCallback);
             mCm.unregisterNetworkCallback(networkCallback);
+            // While requestNetwork increases the count synchronously, unregister decreases it
+            // asynchronously on a handler, so unregistering doesn't immediately free up
+            // a slot : calling unregister-register when max requests are registered throws.
+            // Potential fix : ConnectivityService catches TooManyRequestsException once when
+            // creating NetworkRequestInfo and waits for handler thread (see
+            // https://r.android.com/2707373 for impl). However, this complexity is not equal to
+            // the issue ; the purpose of having "max requests" is only to help apps detect leaks.
+            // Apps relying on exact enforcement or rapid request registration should reconsider.
+            //
+            // In this test, test thread registering all before handler thread decrements can cause
+            // flakes. A single waitForIdle at (e.g.) MAX_REQUESTS / 2 processes decrements up to
+            // that point, fixing the flake.
+            if (MAX_REQUESTS / 2 == i) waitForIdle();
         }
         waitForIdle();
 
@@ -7556,6 +7567,8 @@
             NetworkCallback networkCallback = new NetworkCallback();
             mCm.registerNetworkCallback(networkRequest, networkCallback);
             mCm.unregisterNetworkCallback(networkCallback);
+            // See comment above for the reasons for this wait.
+            if (MAX_REQUESTS / 2 == i) waitForIdle();
         }
         waitForIdle();
 
@@ -7563,6 +7576,8 @@
             NetworkCallback networkCallback = new NetworkCallback();
             mCm.registerDefaultNetworkCallback(networkCallback);
             mCm.unregisterNetworkCallback(networkCallback);
+            // See comment above for the reasons for this wait.
+            if (MAX_REQUESTS / 2 == i) waitForIdle();
         }
         waitForIdle();
 
@@ -7570,6 +7585,8 @@
             NetworkCallback networkCallback = new NetworkCallback();
             mCm.registerDefaultNetworkCallback(networkCallback);
             mCm.unregisterNetworkCallback(networkCallback);
+            // See comment above for the reasons for this wait.
+            if (MAX_REQUESTS / 2 == i) waitForIdle();
         }
         waitForIdle();
 
@@ -7579,6 +7596,8 @@
                 mCm.registerDefaultNetworkCallbackForUid(1000000 + i, networkCallback,
                         new Handler(ConnectivityThread.getInstanceLooper()));
                 mCm.unregisterNetworkCallback(networkCallback);
+                // See comment above for the reasons for this wait.
+                if (MAX_REQUESTS / 2 == i) waitForIdle();
             }
         });
         waitForIdle();
@@ -7588,6 +7607,8 @@
                     mContext, 0 /* requestCode */, new Intent("e" + i), FLAG_IMMUTABLE);
             mCm.requestNetwork(networkRequest, pendingIntent);
             mCm.unregisterNetworkCallback(pendingIntent);
+            // See comment above for the reasons for this wait.
+            if (MAX_REQUESTS / 2 == i) waitForIdle();
         }
         waitForIdle();
 
@@ -7596,6 +7617,8 @@
                     mContext, 0 /* requestCode */, new Intent("f" + i), FLAG_IMMUTABLE);
             mCm.registerNetworkCallback(networkRequest, pendingIntent);
             mCm.unregisterNetworkCallback(pendingIntent);
+            // See comment above for the reasons for this wait.
+            if (MAX_REQUESTS / 2 == i) waitForIdle();
         }
     }
 
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index b943bfc..4f0d46f 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -57,6 +57,7 @@
 import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_ESP;
 import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_UDP;
 import static com.android.testutils.Cleanup.testAndCleanup;
+import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor;
 import static com.android.testutils.MiscAsserts.assertThrows;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -2862,15 +2863,34 @@
         // Verify MOBIKE is triggered
         verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
                 0 /* retryIndex */);
+        // Validation failure on VPN network should trigger a re-evaluation request for the
+        // underlying network.
+        verify(mConnectivityManager).reportNetworkConnectivity(TEST_NETWORK, false);
 
         reset(mIkev2SessionCreator);
+        reset(mExecutor);
 
         // Send validation status update.
         // Recovered and get network validated. It should not trigger the ike session reset.
         ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
                 NetworkAgent.VALIDATION_STATUS_VALID);
+        // Verify that the retry count is reset. The mValidationFailRetryCount will not be reset
+        // until the executor finishes the execute() call, so wait until the all tasks are executed.
+        waitForIdleSerialExecutor(mExecutor, TEST_TIMEOUT_MS);
+        assertEquals(0,
+                ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).mValidationFailRetryCount);
         verify(mIkev2SessionCreator, never()).createIkeSession(
                 any(), any(), any(), any(), any(), any());
+
+        reset(mIkeSessionWrapper);
+        reset(mExecutor);
+
+        // Another validation fail should trigger another reportNetworkConnectivity
+        ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+                NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+        verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
+                0 /* retryIndex */);
+        verify(mConnectivityManager, times(2)).reportNetworkConnectivity(TEST_NETWORK, false);
     }
 
     @Test
@@ -2884,7 +2904,9 @@
                 NetworkAgent.VALIDATION_STATUS_NOT_VALID);
         verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
                 retry++);
-
+        // Validation failure on VPN network should trigger a re-evaluation request for the
+        // underlying network.
+        verify(mConnectivityManager).reportNetworkConnectivity(TEST_NETWORK, false);
         reset(mIkev2SessionCreator);
 
         // Second validation status update.
@@ -2892,6 +2914,8 @@
                 NetworkAgent.VALIDATION_STATUS_NOT_VALID);
         verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
                 retry++);
+        // Call to reportNetworkConnectivity should only happen once. No further interaction.
+        verify(mConnectivityManager, times(1)).reportNetworkConnectivity(TEST_NETWORK, false);
 
         // Use real delay to verify reset session will not be performed if there is an existing
         // recovery for resetting the session.
@@ -2908,6 +2932,8 @@
                 eq(TimeUnit.MILLISECONDS));
         final List<Long> delays = delayCaptor.getAllValues();
         assertEquals(expectedDelay, (long) delays.get(delays.size() - 1));
+        // Call to reportNetworkConnectivity should only happen once. No further interaction.
+        verify(mConnectivityManager, times(1)).reportNetworkConnectivity(TEST_NETWORK, false);
 
         // Another invalid status reported should not trigger other scheduled recovery.
         expectedDelay = mTestDeps.getValidationFailRecoveryMs(retry++);
@@ -2919,6 +2945,8 @@
         // Verify that session being reset
         verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS + expectedDelay))
                 .createIkeSession(any(), any(), any(), any(), any(), any());
+        // Call to reportNetworkConnectivity should only happen once. No further interaction.
+        verify(mConnectivityManager, times(1)).reportNetworkConnectivity(TEST_NETWORK, false);
     }
 
     @Test