blob: 0c04c79a7926479ced9941b0115585ff4af0e133 [file] [log] [blame]
// Copyright 2018 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/application_android.h"
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/jni_utils.h"
#include "starboard/android/shared/log_internal.h"
#include "starboard/common/semaphore.h"
#include "starboard/common/string.h"
#include "starboard/shared/starboard/command_line.h"
#include "starboard/thread.h"
namespace starboard {
namespace android {
namespace shared {
namespace {
using ::starboard::shared::starboard::CommandLine;
typedef ::starboard::android::shared::ApplicationAndroid::AndroidCommand
AndroidCommand;
SbThread g_starboard_thread = kSbThreadInvalid;
// Safeguard to avoid sending AndroidCommands either when there is no instance
// of the Starboard application, or after the run loop has exited and the
// ALooper receiving the commands is no longer being polled.
bool g_app_running = false;
std::vector<std::string> GetArgs() {
std::vector<std::string> args;
// Fake program name as args[0]
args.push_back(SbStringDuplicate("android_main"));
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jobjectArray> args_array(
env->CallStarboardObjectMethodOrAbort("getArgs",
"()[Ljava/lang/String;"));
jint argc = !args_array ? 0 : env->GetArrayLength(args_array.Get());
for (jint i = 0; i < argc; i++) {
ScopedLocalJavaRef<jstring> element(
env->GetObjectArrayElementOrAbort(args_array.Get(), i));
args.push_back(env->GetStringStandardUTFOrAbort(element.Get()));
}
return args;
}
std::string GetStartDeepLink() {
JniEnvExt* env = JniEnvExt::Get();
std::string start_url;
ScopedLocalJavaRef<jstring> j_url(env->CallStarboardObjectMethodOrAbort(
"getStartDeepLink", "()Ljava/lang/String;"));
if (j_url) {
start_url = env->GetStringStandardUTFOrAbort(j_url.Get());
}
SB_LOG(INFO) << "GetStartDeepLink: " << start_url;
return start_url;
}
void* ThreadEntryPoint(void* context) {
Semaphore* app_created_semaphore = static_cast<Semaphore*>(context);
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ApplicationAndroid app(looper);
CommandLine command_line(GetArgs());
LogInit(command_line);
// Mark the app running before signaling app created so there's no race to
// allow sending the first AndroidCommand after onCreate() returns.
g_app_running = true;
// Signal ANativeActivity_onCreate() that it may proceed.
app_created_semaphore->Put();
// Enter the Starboard run loop until stopped.
int error_level =
app.Run(std::move(command_line), GetStartDeepLink().c_str());
// Mark the app not running before informing StarboardBridge that the app is
// stopped so that we won't send any more AndroidCommands as a result of
// shutting down the Activity.
g_app_running = false;
// Our launcher.py looks for this to know when the app (test) is done.
SB_LOG(INFO) << "***Application Stopped*** " << error_level;
// Inform StarboardBridge that the run loop has exited so it can cleanup and
// kill the process.
JniEnvExt* env = JniEnvExt::Get();
env->CallStarboardVoidMethodOrAbort("afterStopped", "()V");
return NULL;
}
void OnStart(ANativeActivity* activity) {
if (g_app_running) {
ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kStart);
}
}
void OnResume(ANativeActivity* activity) {
if (g_app_running) {
// Stop the MediaPlaybackService if activity state transits from background
// to foreground. Note that the MediaPlaybackService may already have
// been stopped before Cobalt's lifecycle state transits from Concealed
// to Frozen.
ApplicationAndroid::Get()->StopMediaPlaybackService();
ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kResume);
}
}
void OnPause(ANativeActivity* activity) {
if (g_app_running) {
// Start the MediaPlaybackService before activity state transits from
// foreground to background.
ApplicationAndroid::Get()->StartMediaPlaybackService();
ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kPause);
}
}
void OnStop(ANativeActivity* activity) {
if (g_app_running) {
ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kStop);
}
}
void OnWindowFocusChanged(ANativeActivity* activity, int focused) {
if (g_app_running) {
ApplicationAndroid::Get()->SendAndroidCommand(
focused ? AndroidCommand::kWindowFocusGained
: AndroidCommand::kWindowFocusLost);
}
}
void OnNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
if (g_app_running) {
ApplicationAndroid::Get()->SendAndroidCommand(
AndroidCommand::kNativeWindowCreated, window);
}
}
void OnNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
if (g_app_running) {
ApplicationAndroid::Get()->SendAndroidCommand(
AndroidCommand::kNativeWindowDestroyed);
}
}
void OnInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
if (g_app_running) {
ApplicationAndroid::Get()->SendAndroidCommand(
AndroidCommand::kInputQueueChanged, queue);
}
}
void OnInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
if (g_app_running) {
ApplicationAndroid::Get()->SendAndroidCommand(
AndroidCommand::kInputQueueChanged, NULL);
}
}
extern "C" SB_EXPORT_PLATFORM void ANativeActivity_onCreate(
ANativeActivity* activity,
void* savedState,
size_t savedStateSize) {
// Start the Starboard thread the first time an Activity is created.
if (!SbThreadIsValid(g_starboard_thread)) {
Semaphore semaphore;
g_starboard_thread =
SbThreadCreate(0, kSbThreadPriorityNormal, kSbThreadNoAffinity, false,
"StarboardMain", &ThreadEntryPoint, &semaphore);
// Wait for the ApplicationAndroid to be created.
semaphore.Take();
}
activity->callbacks->onStart = OnStart;
activity->callbacks->onResume = OnResume;
activity->callbacks->onPause = OnPause;
activity->callbacks->onStop = OnStop;
activity->callbacks->onWindowFocusChanged = OnWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = OnNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = OnNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = OnInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = OnInputQueueDestroyed;
activity->instance = ApplicationAndroid::Get();
}
extern "C" SB_EXPORT_PLATFORM jboolean
Java_dev_cobalt_coat_StarboardBridge_nativeIsReleaseBuild() {
#if defined(COBALT_BUILD_TYPE_GOLD)
return true;
#else
return false;
#endif
}
extern "C" SB_EXPORT_PLATFORM void
Java_dev_cobalt_coat_StarboardBridge_nativeInitialize(
JniEnvExt* env,
jobject starboard_bridge) {
JniEnvExt::Initialize(env, starboard_bridge);
}
} // namespace
} // namespace shared
} // namespace android
} // namespace starboard