Introduce a LazyJniRegistrar helper class for system server

Currently, system server registers all JNI native method entrypoints
eagerly in SystemServer init. This has generally been fine, but doesn't
allow for any flexibility in the existence of Java classes defining
these entrypoints; it's assumed that all such entrypoints always exist.

The LazyJniRegistrar class allows lazy registration of JNI methods
for classes or services, shifting registration responsibilty to
the Java classes that use these entrypoints. This in turn allows
optional classes or services to be stripped from the build.

Start by migrating only a small subset of classes that may be
considered optional in the near future. Eventually, we might
consider migrating more classes to further streamline init and
avoid cascading initialization overhead.

Bug: 375264322
Test: m + presubmit
Flag: EXEMPT refactor
Change-Id: Ia2a0f52575cd47b0f418bb0439ab3a02d413b6ae
diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java
index ee6d808..8362079 100644
--- a/services/core/java/com/android/server/ConsumerIrService.java
+++ b/services/core/java/com/android/server/ConsumerIrService.java
@@ -30,6 +30,8 @@
 import android.os.ServiceManager;
 import android.util.Slog;
 
+import com.android.server.utils.LazyJniRegistrar;
+
 public class ConsumerIrService extends IConsumerIrService.Stub {
     private static final String TAG = "ConsumerIrService";
 
@@ -39,6 +41,10 @@
     private static native int halTransmit(int carrierFrequency, int[] pattern);
     private static native int[] halGetCarrierFrequencies();
 
+    static {
+        LazyJniRegistrar.registerConsumerIrService();
+    }
+
     private final Context mContext;
     private final PowerManager.WakeLock mWakeLock;
     private final boolean mHasNativeHal;
diff --git a/services/core/java/com/android/server/utils/LazyJniRegistrar.java b/services/core/java/com/android/server/utils/LazyJniRegistrar.java
new file mode 100644
index 0000000..ac4a92e
--- /dev/null
+++ b/services/core/java/com/android/server/utils/LazyJniRegistrar.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.android.server.utils;
+
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.MethodAccessFlags;
+import com.android.tools.r8.keepanno.annotations.UsedByNative;
+
+/**
+ * Utility class for lazily registering native methods for a given class.
+ *
+ * <p><strong>Note: </strong>Most native methods are registered eagerly via the
+ * native {@code JNI_OnLoad} hook when system server loads its primary native
+ * lib. However, some classes within system server may be stripped if unused.
+ * This class offers a way to selectively register their native methods. Such
+ * register calls should typically be done from that class's {@code static {}}
+ * init block.
+ */
+@UsedByNative(
+        description = "Referenced from JNI in jni/com_android_server_utils_LazyJniRegistrar.cpp",
+        kind = KeepItemKind.CLASS_AND_MEMBERS,
+        methodAccess = {MethodAccessFlags.NATIVE})
+public final class LazyJniRegistrar {
+
+    // Note: {@link SystemServer#run} loads the native "android_servers" lib, so no need to do so
+    // explicitly here. Classes that use this registration must not be initialized before this.
+
+    /** Registers native methods for ConsumerIrService. */
+    public static native void registerConsumerIrService();
+
+    /** Registers native methods for VrManagerService. */
+    public static native void registerVrManagerService();
+}
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index fbc0b56..9f1cc81 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -10,6 +10,7 @@
 per-file Watcher.java = shombert@google.com
 per-file EventLogger.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
 per-file EventLogger.java = jmtrivi@google.com
+per-file LazyJniRegistrar.java = file:/PERFORMANCE_OWNERS
 
 # Bug component : 158088 = per-file AnrTimer*.java
 per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 1ff01a6..9cfe3ba 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -67,6 +67,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
+import com.android.server.utils.LazyJniRegistrar;
 import com.android.server.utils.ManagedApplicationService;
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
 import com.android.server.utils.ManagedApplicationService.LogEvent;
@@ -131,6 +132,10 @@
     private static native void initializeNative();
     private static native void setVrModeNative(boolean enabled);
 
+    static {
+        LazyJniRegistrar.registerVrManagerService();
+    }
+
     private final Object mLock = new Object();
 
     private final IBinder mOverlayToken = new Binder();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7a710dc..ab987ad 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -76,6 +76,7 @@
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
         "com_android_server_sensor_SensorService.cpp",
+        "com_android_server_utils_LazyJniRegistrar.cpp",
         "com_android_server_wm_TaskFpsCallbackController.cpp",
         "onload.cpp",
         ":lib_cachedAppOptimizer_native",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index b622751..8052b09 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -30,6 +30,7 @@
 per-file com_android_server_am_CachedAppOptimizer.cpp = file:/PERFORMANCE_OWNERS
 per-file com_android_server_am_Freezer.cpp = file:/PERFORMANCE_OWNERS
 per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
+per-file com_android_server_utils_LazyJniRegistrar.cpp = file:/PERFORMANCE_OWNERS
 
 # Memory
 per-file com_android_server_am_OomConnection.cpp = file:/MEMORY_OWNERS
diff --git a/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp
new file mode 100644
index 0000000..ad7781e
--- /dev/null
+++ b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include <nativehelper/JNIHelp.h>
+
+#include "jni.h"
+
+namespace android {
+
+// Forward declared per-class registration methods.
+int register_android_server_ConsumerIrService(JNIEnv* env);
+int register_android_server_vr_VrManagerService(JNIEnv* env);
+
+namespace {
+
+// TODO(b/)375264322: Remove these trampoline methods after finalizing the
+// registrar implementation. Instead, just update the called methods to take a
+// class arg, and hand those methods to jniRegisterNativeMethods directly.
+void registerConsumerIrService(JNIEnv* env, jclass) {
+    register_android_server_ConsumerIrService(env);
+}
+
+void registerVrManagerService(JNIEnv* env, jclass) {
+    register_android_server_vr_VrManagerService(env);
+}
+
+static const JNINativeMethod sJniRegistrarMethods[] = {
+        {"registerConsumerIrService", "()V", (void*)registerConsumerIrService},
+        {"registerVrManagerService", "()V", (void*)registerVrManagerService},
+};
+
+} // namespace
+
+int register_android_server_utils_LazyJniRegistrar(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/server/utils/LazyJniRegistrar",
+                                    sJniRegistrarMethods, NELEM(sJniRegistrarMethods));
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 6464081..6329b8c 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -25,7 +25,6 @@
 
 namespace android {
 int register_android_server_BatteryStatsService(JNIEnv* env);
-int register_android_server_ConsumerIrService(JNIEnv *env);
 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
@@ -39,7 +38,6 @@
 int register_android_server_UsbAlsaMidiDevice(JNIEnv* env);
 int register_android_server_UsbDeviceManager(JNIEnv* env);
 int register_android_server_UsbHostManager(JNIEnv* env);
-int register_android_server_vr_VrManagerService(JNIEnv* env);
 int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env);
 int register_android_server_vibrator_VibratorManagerService(JavaVM* vm, JNIEnv* env);
 int register_android_server_location_GnssLocationProvider(JNIEnv* env);
@@ -56,6 +54,7 @@
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
 int register_android_server_am_LowMemDetector(JNIEnv* env);
 int register_android_server_utils_AnrTimer(JNIEnv *env);
+int register_android_server_utils_LazyJniRegistrar(JNIEnv* env);
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
 int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(JNIEnv* env);
 int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
@@ -72,6 +71,9 @@
 int register_com_android_server_SystemClockTime(JNIEnv* env);
 int register_android_server_display_smallAreaDetectionController(JNIEnv* env);
 int register_com_android_server_accessibility_BrailleDisplayConnection(JNIEnv* env);
+
+// Note: Consider adding new JNI entrypoints for optional services to
+// LazyJniRegistrar instead, and relying on lazy registration.
 };
 
 using namespace android;
@@ -100,14 +102,12 @@
     register_android_server_UsbAlsaJackDetector(env);
     register_android_server_UsbAlsaMidiDevice(env);
     register_android_server_UsbHostManager(env);
-    register_android_server_vr_VrManagerService(env);
     register_android_server_vibrator_VibratorController(vm, env);
     register_android_server_vibrator_VibratorManagerService(vm, env);
     register_android_server_SystemServer(env);
     register_android_server_location_GnssLocationProvider(env);
     register_android_server_connectivity_Vpn(env);
     register_android_server_devicepolicy_CryptoTestHelper(env);
-    register_android_server_ConsumerIrService(env);
     register_android_server_BatteryStatsService(env);
     register_android_server_tv_TvUinputBridge(env);
     register_android_server_tv_TvInputHal(env);
@@ -120,6 +120,7 @@
     register_android_server_am_CachedAppOptimizer(env);
     register_android_server_am_LowMemDetector(env);
     register_android_server_utils_AnrTimer(env);
+    register_android_server_utils_LazyJniRegistrar(env);
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
     register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(env);
     register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);