| /* |
| * 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 |