blob: e0de789ea8743874b6f8031b87ef747d5a494da7 [file]
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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 <cstdlib>
#include "KString.h"
#include "Memory.h"
#include "Natives.h"
#include "Porting.h"
#include "Runtime.h"
#include "Types.h"
#ifdef KONAN_ANDROID
#include <unistd.h>
#include <pthread.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "launcher.h"
#include "androidLauncher.h"
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "Konan_main", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "Konan_main", __VA_ARGS__))
/* For debug builds, always enable the debug traces in this library */
#ifndef NDEBUG
# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "Konan_main", __VA_ARGS__))
#else
# define LOGV(...) ((void)0)
#endif
using namespace kotlin;
//--- main --------------------------------------------------------------------//
namespace {
typedef struct {
int pipeC;
int pipeKonan;
NativeActivityState nativeActivityState;
} LauncherState;
LauncherState* launcherState = nullptr;
}
extern "C" void getNativeActivityState(NativeActivityState* state) {
state->activity = launcherState->nativeActivityState.activity;
state->savedState = launcherState->nativeActivityState.savedState;
state->savedStateSize = launcherState->nativeActivityState.savedStateSize;
state->looper = launcherState->nativeActivityState.looper;
}
extern "C" void notifySysEventProcessed() {
int8_t message;
write(launcherState->pipeKonan, &message, sizeof(message));
}
namespace {
void launchMain() {
Kotlin_initRuntimeIfNeeded();
Kotlin_mm_switchThreadStateRunnable();
{
ObjHolder args;
AllocArrayInstance(theArrayTypeInfo, 0, args.slot());
Konan_start(args.obj());
}
// TODO: Can we shutdown runtime here?
deinitRuntimeIfNeeded();
}
void* entry(void* param) {
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, launcherState->pipeKonan, LOOPER_ID_SYS, ALOOPER_EVENT_INPUT, NULL, NULL);
launcherState->nativeActivityState.looper = looper;
launchMain();
return nullptr;
}
void runKonan_start(bool startThread) {
if (!startThread) {
launchMain();
return;
}
int pipes[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes)) {
LOGE("Could not create pipe: %s", strerror(errno));
return;
}
launcherState->pipeC = pipes[0];
launcherState->pipeKonan = pipes[1];
LOGE("runKonan_start() %d %d", launcherState->pipeC, launcherState->pipeKonan);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t thread;
pthread_create(&thread, &attr, entry, nullptr);
}
void putEventSynchronously(void* event) {
auto value = reinterpret_cast<uintptr_t>(event);
if (write(launcherState->pipeC, &value, sizeof(value)) != sizeof(value)) {
LOGE("Failure writing event: %s\n", strerror(errno));
}
int8_t response;
if (read(launcherState->pipeC, &response, sizeof(response)) != sizeof(response)) {
LOGE("Failure reading response: %s\n", strerror(errno));
}
}
void onDestroy(ANativeActivity* activity) {
LOGV("onDestroy called");
NativeActivityEvent event = { DESTROY };
putEventSynchronously(&event);
}
void onStart(ANativeActivity* activity) {
LOGV("onStart called");
NativeActivityEvent event = { START };
putEventSynchronously(&event);
}
void onResume(ANativeActivity* activity) {
LOGV("onResume called");
NativeActivitySaveStateEvent event = { RESUME, nullptr, 0 };
putEventSynchronously(&event);
}
void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
LOGV("onSaveInstanceState called");
NativeActivitySaveStateEvent event = { SAVE_INSTANCE_STATE, nullptr, 0 };
putEventSynchronously(&event);
*outLen = event.savedStateSize;
return event.savedState;
}
void onPause(ANativeActivity* activity) {
LOGV("onPause called");
NativeActivityEvent event = { PAUSE };
putEventSynchronously(&event);
}
void onStop(ANativeActivity* activity) {
LOGV("onStop called");
NativeActivityEvent event = { STOP };
putEventSynchronously(&event);
}
void onConfigurationChanged(ANativeActivity* activity) {
LOGV("onConfigurationChanged called");
NativeActivityEvent event = { CONFIGURATION_CHANGED };
putEventSynchronously(&event);
}
void onLowMemory(ANativeActivity* activity) {
LOGV("onLowMemory called");
NativeActivityEvent event = { LOW_MEMORY };
putEventSynchronously(&event);
}
void onWindowFocusChanged(ANativeActivity* activity, int focused) {
LOGV("onWindowFocusChanged called");
NativeActivityEvent event = { focused ? WINDOW_GAINED_FOCUS : WINDOW_LOST_FOCUS };
putEventSynchronously(&event);
}
void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
LOGV("onNativeWindowCreated called");
NativeActivityWindowEvent event = { NATIVE_WINDOW_CREATED, window };
putEventSynchronously(&event);
}
void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
LOGV("onNativeWindowDestroyed called");
NativeActivityWindowEvent event = { NATIVE_WINDOW_DESTROYED, window };
putEventSynchronously(&event);
}
void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
LOGV("onInputQueueCreated called");
NativeActivityQueueEvent event = { INPUT_QUEUE_CREATED, queue };
putEventSynchronously(&event);
}
void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
LOGV("onInputQueueDestroyed called");
NativeActivityQueueEvent event = { INPUT_QUEUE_DESTROYED, queue };
putEventSynchronously(&event);
}
}
extern "C" void RUNTIME_EXPORT Konan_main(
ANativeActivity* activity, void* savedState, size_t savedStateSize) {
bool launchThread = activity->instance == nullptr;
if (launchThread) {
launcherState = (LauncherState*)std::calloc(sizeof(LauncherState), 1);
launcherState->nativeActivityState = {activity, savedState, savedStateSize, nullptr};
activity->instance = launcherState;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
} else {
launcherState = (LauncherState*)activity->instance;
}
runKonan_start(launchThread);
}
#endif // KONAN_ANDROID