diff --git a/cobalt/android/BUILD.gn b/cobalt/android/BUILD.gn index 85e5e0d69d7..831a54795fc 100644 --- a/cobalt/android/BUILD.gn +++ b/cobalt/android/BUILD.gn @@ -32,13 +32,21 @@ jinja_template("cobalt_manifest") { variables = [ "manifest_package=dev.cobalt.coat" ] } +generate_jni("jni_headers") { + sources = [ "apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java" ] +} + # TODO(cobalt): Re-enable or remove disabled java files. android_library("cobalt_apk_java") { testonly = true resources_package = "dev.cobalt.coat" + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] + deps = [ ":cobalt_java_resources", + ":jni_headers", "//base:base_java", + "//base:jni_java", "//base:process_launcher_java", "//build/android:build_java", "//components/embedder_support/android:view_java", @@ -57,6 +65,7 @@ android_library("cobalt_apk_java") { "//ui/android:ui_no_recycler_view_java", "//url:gurl_java", ] + sources = [ "apk/app/src/app/java/dev/cobalt/app/CobaltApplication.java", "apk/app/src/app/java/dev/cobalt/app/MainActivity.java", diff --git a/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java index d94590f0052..aba29188336 100644 --- a/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java +++ b/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java @@ -52,8 +52,12 @@ import java.util.HashMap; import java.util.Locale; import java.util.TimeZone; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; /** Implementation of the required JNI methods called by the Starboard C++ code. */ +@JNINamespace("starboard::android::shared") public class StarboardBridge { /** Interface to be implemented by the Android Application hosting the starboard app. */ @@ -144,7 +148,19 @@ public StarboardBridge( private native void closeNativeStarboard(long nativeApp); - private native long nativeCurrentMonotonicTime(); + @NativeMethods + interface Natives { + void onStop(); + + long currentMonotonicTime(); + + // TODO(cobalt, b/372559388): move below native methods to the Natives interface. + // boolean initJNI(); + + // long startNativeStarboard(); + + // void closeNativeStarboard(long nativeApp); + } protected void onActivityStart(Activity activity) { Log.e(TAG, "onActivityStart ran"); @@ -163,8 +179,6 @@ protected void onActivityStop(Activity activity) { afterStopped(); } - private native void nativeOnStop(); - protected void onActivityDestroy(Activity activity) { if (applicationStopped) { // We can't restart the starboard app, so kill the process for a clean start next time. @@ -240,13 +254,13 @@ protected void afterStopped() { } @SuppressWarnings("unused") - @UsedByNative + @CalledByNative protected void applicationStarted() { applicationReady = true; } @SuppressWarnings("unused") - @UsedByNative + @CalledByNative protected void applicationStopping() { applicationReady = false; applicationStopped = true; @@ -362,6 +376,9 @@ protected String getCacheAbsolutePath() { */ @SuppressWarnings("unused") @UsedByNative + // TODO: (cobalt b/372559388) Migrate complicated returned type functions to JNI zero. + // The @UsedByNative annotation has strict signature parsing rules, + // and Pair is not be supported well. Pair getLocalInterfaceAddressAndNetmask(boolean wantIPv6) { try { Enumeration it = NetworkInterface.getNetworkInterfaces(); @@ -780,12 +797,12 @@ public byte[] sendToCobaltService(String serviceName, byte [] data) { /** Returns the application start timestamp. */ @SuppressWarnings("unused") - @UsedByNative + @CalledByNative protected long getAppStartTimestamp() { Activity activity = activityHolder.get(); if (activity instanceof CobaltActivity) { long javaStartTimestamp = ((CobaltActivity) activity).getAppStartTimestamp(); - long cppTimestamp = nativeCurrentMonotonicTime(); + long cppTimestamp = StarboardBridgeJni.get().currentMonotonicTime(); long javaStopTimestamp = System.nanoTime(); return cppTimestamp - (javaStopTimestamp - javaStartTimestamp) / timeNanosecondsPerMicrosecond; diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn index eef374515f4..c51409bf22c 100644 --- a/starboard/android/shared/BUILD.gn +++ b/starboard/android/shared/BUILD.gn @@ -265,6 +265,8 @@ static_library("starboard_platform") { #"speech_synthesis_internal.cc", #"speech_synthesis_is_supported.cc", #"speech_synthesis_speak.cc", + "starboard_bridge.cc", + "starboard_bridge.h", "system_get_extensions.cc", "system_get_locale_id.cc", "system_get_path.cc", @@ -316,12 +318,14 @@ static_library("starboard_platform") { public_deps = [ ":starboard_base_symbolize", ":starboard_jni_state", + "//cobalt/android:jni_headers", "//starboard/common", "//starboard/shared/starboard/media:media_util", "//starboard/shared/starboard/player/filter:filter_based_player_sources", ] deps = [ + "//base", "//third_party/libevent", "//third_party/opus", ] diff --git a/starboard/android/shared/android_main.cc b/starboard/android/shared/android_main.cc index 0ed8090673a..b16001e7a70 100644 --- a/starboard/android/shared/android_main.cc +++ b/starboard/android/shared/android_main.cc @@ -35,6 +35,8 @@ #include "starboard/crashpad_wrapper/wrapper.h" // nogncheck #endif +#include "starboard/android/shared/starboard_bridge.h" + namespace starboard { namespace android { namespace shared { @@ -292,10 +294,15 @@ void StarboardThreadLaunch() { } #endif // SB_IS(EVERGREEN_COMPATIBLE) +// TODO(cobalt, b/372559388): consolidate this function when fully deprecate +// JniEnvExt. extern "C" SB_EXPORT_PLATFORM void Java_dev_cobalt_coat_StarboardBridge_initJNI( JniEnvExt* env, jobject starboard_bridge) { JniEnvExt::Initialize(env, starboard_bridge); + + // Initialize the singleton instance of StarboardBridge + StarboardBridge::GetInstance()->Initialize(env, starboard_bridge); } extern "C" SB_EXPORT_PLATFORM jlong diff --git a/starboard/android/shared/application_android.cc b/starboard/android/shared/application_android.cc index 5d076a7f268..1ac2878e532 100644 --- a/starboard/android/shared/application_android.cc +++ b/starboard/android/shared/application_android.cc @@ -24,6 +24,7 @@ #include #include +#include "base/android/jni_android.h" #include "starboard/extension/accessibility.h" #include "starboard/android/shared/file_internal.h" @@ -44,14 +45,6 @@ namespace starboard { namespace android { namespace shared { -namespace { -int64_t GetAppStartTimestamp() { - JniEnvExt* env = JniEnvExt::Get(); - jlong app_start_timestamp = - env->CallStarboardLongMethodOrAbort("getAppStartTimestamp", "()J"); - return app_start_timestamp; -} -} // namespace // TODO(cobalt, b/378708359): Remove this dummy init. void stubSbEventHandle(const SbEvent* event) { @@ -74,14 +67,16 @@ ApplicationAndroid::ApplicationAndroid( jobject local_ref = env->CallStarboardObjectMethodOrAbort( "getResourceOverlay", "()Ldev/cobalt/coat/ResourceOverlay;"); resource_overlay_ = env->ConvertLocalRefToGlobalRef(local_ref); + SbAudioSinkPrivate::Initialize(); - app_start_timestamp_ = GetAppStartTimestamp(); - env->CallStarboardVoidMethodOrAbort("applicationStarted", "()V"); + + app_start_timestamp_ = starboard_bridge_->GetAppStartTimestamp(); + + starboard_bridge_->ApplicationStarted(); } ApplicationAndroid::~ApplicationAndroid() { - JniEnvExt* env = JniEnvExt::Get(); - env->CallStarboardVoidMethodOrAbort("applicationStopping", "()V"); + starboard_bridge_->ApplicationStopping(); // The application is exiting. // Release the global reference. @@ -94,12 +89,6 @@ ApplicationAndroid::~ApplicationAndroid() { JniEnvExt::OnThreadShutdown(); } -extern "C" SB_EXPORT_PLATFORM void -Java_dev_cobalt_coat_StarboardBridge_nativeOnStop(JniEnvExt* env) { - SbAudioSinkPrivate::TearDown(); - SbFileAndroidTeardown(); -} - extern "C" SB_EXPORT_PLATFORM jboolean Java_dev_cobalt_coat_StarboardBridge_nativeOnSearchRequested( JniEnvExt* env, @@ -108,14 +97,6 @@ Java_dev_cobalt_coat_StarboardBridge_nativeOnSearchRequested( return true; } -extern "C" SB_EXPORT_PLATFORM jlong -Java_dev_cobalt_coat_StarboardBridge_nativeCurrentMonotonicTime( - JNIEnv* env, - jobject jcaller, - jboolean online) { - return CurrentMonotonicTime(); -} - extern "C" SB_EXPORT_PLATFORM void Java_dev_cobalt_coat_CobaltSystemConfigChangeReceiver_nativeDateTimeConfigurationChanged( JNIEnv* env, diff --git a/starboard/android/shared/application_android.h b/starboard/android/shared/application_android.h index 16c2a577840..0585457bef3 100644 --- a/starboard/android/shared/application_android.h +++ b/starboard/android/shared/application_android.h @@ -29,6 +29,8 @@ #include "starboard/shared/starboard/queue_application.h" #include "starboard/types.h" +#include "starboard/android/shared/starboard_bridge.h" + namespace starboard { namespace android { namespace shared { @@ -66,6 +68,10 @@ class ApplicationAndroid void WakeSystemEventWait() override {} private: + // starboard_bridge_ is a global singleton, use a raw pointer to not interfere + // with it's lifecycle management. + const raw_ptr starboard_bridge_ = + StarboardBridge::GetInstance(); jobject resource_overlay_; Mutex overlay_mutex_; diff --git a/starboard/android/shared/starboard_bridge.cc b/starboard/android/shared/starboard_bridge.cc new file mode 100644 index 00000000000..d592e3b2ec7 --- /dev/null +++ b/starboard/android/shared/starboard_bridge.cc @@ -0,0 +1,67 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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 "starboard/android/shared/starboard_bridge.h" + +#include "starboard/android/shared/file_internal.h" +#include "starboard/common/time.h" +#include "starboard/media.h" +#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h" + +// Must come after all headers that specialize FromJniType() / ToJniType(). +#include "cobalt/android/jni_headers/StarboardBridge_jni.h" + +namespace starboard { +namespace android { +namespace shared { + +extern "C" SB_EXPORT_PLATFORM void JNI_StarboardBridge_OnStop(JNIEnv* env) { + SbAudioSinkPrivate::TearDown(); + SbFileAndroidTeardown(); +} + +extern "C" SB_EXPORT_PLATFORM jlong +JNI_StarboardBridge_CurrentMonotonicTime(JNIEnv* env) { + return CurrentMonotonicTime(); +} + +// static +StarboardBridge* StarboardBridge::GetInstance() { + return base::Singleton::get(); +} + +void StarboardBridge::Initialize(JNIEnv* env, jobject obj) { + j_starboard_bridge_.Reset(env, obj); +} + +long StarboardBridge::GetAppStartTimestamp() { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + return Java_StarboardBridge_getAppStartTimestamp(env, j_starboard_bridge_); +} + +void StarboardBridge::ApplicationStarted() { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + return Java_StarboardBridge_applicationStarted(env, j_starboard_bridge_); +} + +void StarboardBridge::ApplicationStopping() { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + return Java_StarboardBridge_applicationStopping(env, j_starboard_bridge_); +} +} // namespace shared +} // namespace android +} // namespace starboard diff --git a/starboard/android/shared/starboard_bridge.h b/starboard/android/shared/starboard_bridge.h new file mode 100644 index 00000000000..fec41feb88f --- /dev/null +++ b/starboard/android/shared/starboard_bridge.h @@ -0,0 +1,60 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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. + +#ifndef STARBOARD_ANDROID_SHARED_STARBOARD_BRIDGE_H_ +#define STARBOARD_ANDROID_SHARED_STARBOARD_BRIDGE_H_ + +#include + +#include "base/android/scoped_java_ref.h" +#include "base/memory/singleton.h" + +namespace starboard { +namespace android { +namespace shared { + +// This class serves as a bridge between the native code and Android +// StarboardBridge Java class. +class StarboardBridge { + public: + // Returns the singleton. + static StarboardBridge* GetInstance(); + + void Initialize(JNIEnv* env, jobject obj); + + long GetAppStartTimestamp(); + + void ApplicationStarted(); + + void ApplicationStopping(); + + private: + StarboardBridge() = default; + ~StarboardBridge() = default; + + // Prevent copy construction and assignment + StarboardBridge(const StarboardBridge&) = delete; + StarboardBridge& operator=(const StarboardBridge&) = delete; + + friend struct base::DefaultSingletonTraits; + + // Java StarboardBridge instance. + base::android::ScopedJavaGlobalRef j_starboard_bridge_; +}; + +} // namespace shared +} // namespace android +} // namespace starboard + +#endif // STARBOARD_ANDROID_SHARED_STARBOARD_BRIDGE_H_