16#include <freerdp/config.h>
25#include <winpr/assert.h>
26#include <winpr/image.h>
28#include <freerdp/graphics.h>
29#include <freerdp/codec/rfx.h>
30#include <freerdp/gdi/gdi.h>
31#include <freerdp/gdi/gfx.h>
32#include <freerdp/client/rdpei.h>
33#include <freerdp/client/rdpgfx.h>
34#include <freerdp/client/cliprdr.h>
35#include <freerdp/codec/h264.h>
36#include <freerdp/codec/video.h>
37#include <freerdp/channels/channels.h>
38#include <freerdp/client/channels.h>
39#include <freerdp/client/cmdline.h>
40#include <freerdp/constants.h>
41#include <freerdp/locale/keyboard.h>
42#include <freerdp/primitives.h>
43#include <freerdp/version.h>
44#include <freerdp/settings.h>
45#include <freerdp/utils/signal.h>
47#include <android/bitmap.h>
49#include "android_jni_callback.h"
50#include "android_jni_utils.h"
51#include "android_cliprdr.h"
52#include "android_disp.h"
53#include "android_rail.h"
54#include "android_freerdp_jni.h"
56#if defined(WITH_GPROF)
60#define TAG CLIENT_TAG("android")
63#define FREERDP_JNI_VERSION FREERDP_VERSION_FULL
65static jclass gJavaActivityClass;
66static jmethodID gOnPointerSetMethod;
67static jmethodID gOnRailWindowUpdateMethod;
69static UINT android_UpdateWindowFromSurface(RdpgfxClientContext* context, gdiGfxSurface* surface);
71static void android_OnChannelConnectedEventHandler(
void* context,
72 const ChannelConnectedEventArgs* e)
74 rdpSettings* settings;
79 WLog_FATAL(TAG,
"(context=%p, EventArgs=%p", context, (
void*)e);
84 settings = afc->common.context.settings;
86 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
88 android_cliprdr_init(afc, (CliprdrClientContext*)e->pInterface);
90 else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
92 android_disp_init(afc, (DispClientContext*)e->pInterface);
94 else if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) == 0)
96 android_rail_init(afc, (RailClientContext*)e->pInterface);
98 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
100 freerdp_client_OnChannelConnectedEventHandler(context, e);
101 RdpgfxClientContext* gfx = (RdpgfxClientContext*)e->pInterface;
103 gfx->UpdateWindowFromSurface = android_UpdateWindowFromSurface;
106 freerdp_client_OnChannelConnectedEventHandler(context, e);
109static void android_OnChannelDisconnectedEventHandler(
void* context,
110 const ChannelDisconnectedEventArgs* e)
112 rdpSettings* settings;
117 WLog_FATAL(TAG,
"(context=%p, EventArgs=%p", context, (
void*)e);
122 settings = afc->common.context.settings;
124 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
126 android_cliprdr_uninit(afc, (CliprdrClientContext*)e->pInterface);
128 else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
130 android_disp_uninit(afc, (DispClientContext*)e->pInterface);
132 else if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) == 0)
134 android_rail_uninit(afc, (RailClientContext*)e->pInterface);
137 freerdp_client_OnChannelDisconnectedEventHandler(context, e);
140static BOOL android_begin_paint(rdpContext* context)
145static BOOL android_end_paint(rdpContext* context)
153 rdpSettings* settings;
155 if (!ctx || !context->instance)
158 settings = context->settings;
165 if (!gdi || !gdi->primary || !gdi->primary->hdc)
168 hwnd = ctx->common.context.gdi->primary->hdc->hwnd;
173 ninvalid = hwnd->ninvalid;
178 cinvalid = hwnd->cinvalid;
185 x2 = cinvalid[0].x + cinvalid[0].w;
186 y2 = cinvalid[0].y + cinvalid[0].h;
188 for (
int i = 0; i < ninvalid; i++)
190 x1 = MIN(x1, cinvalid[i].x);
191 y1 = MIN(y1, cinvalid[i].y);
192 x2 = MAX(x2, cinvalid[i].x + cinvalid[i].w);
193 y2 = MAX(y2, cinvalid[i].y + cinvalid[i].h);
196 freerdp_callback(
"OnGraphicsUpdate",
"(JIIII)V", (jlong)context->instance, x1, y1, x2 - x1,
199 hwnd->invalid->null = TRUE;
204static BOOL android_desktop_resize(rdpContext* context)
206 WINPR_ASSERT(context);
207 WINPR_ASSERT(context->settings);
208 WINPR_ASSERT(context->instance);
213 if (context->gdi && !gdi_resize(context->gdi, width, height))
216 freerdp_callback(
"OnGraphicsResize",
"(JIII)V", (jlong)context->instance, width, height,
221static BOOL android_pre_connect(freerdp* instance)
223 WINPR_ASSERT(instance);
224 WINPR_ASSERT(instance->context);
226 rdpSettings* settings = instance->context->settings;
231 int rc = PubSub_SubscribeChannelConnected(instance->context->pubSub,
232 android_OnChannelConnectedEventHandler);
234 if (rc != CHANNEL_RC_OK)
236 WLog_ERR(TAG,
"Could not subscribe to connect event handler [%08X]", rc);
240 rc = PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
241 android_OnChannelDisconnectedEventHandler);
243 if (rc != CHANNEL_RC_OK)
245 WLog_ERR(TAG,
"Could not subscribe to disconnect event handler [%08X]", rc);
249 freerdp_callback(
"OnPreConnect",
"(J)V", (jlong)instance);
260static BOOL android_Pointer_New(rdpContext* context, rdpPointer* pointer)
262 WINPR_ASSERT(context);
263 WINPR_ASSERT(pointer);
264 WINPR_ASSERT(context->gdi);
266 androidPointer* ptr = (androidPointer*)pointer;
270 ptr->size = 4ULL * pointer->width * pointer->height;
271 ptr->data = winpr_aligned_malloc(ptr->size, 16);
275 if (!freerdp_image_copy_from_pointer_data(
276 ptr->data, PIXEL_FORMAT_BGRA32, 0, 0, 0, pointer->width, pointer->height,
277 pointer->xorMaskData, pointer->lengthXorMask, pointer->andMaskData,
278 pointer->lengthAndMask, pointer->xorBpp, &context->gdi->palette))
280 winpr_aligned_free(ptr->data);
288static void android_Pointer_Free(rdpContext* context, rdpPointer* pointer)
290 WINPR_UNUSED(context);
291 androidPointer* ptr = (androidPointer*)pointer;
295 winpr_aligned_free(ptr->data);
300static BOOL android_Pointer_Set(rdpContext* context, rdpPointer* pointer)
302 WINPR_ASSERT(context);
303 WINPR_ASSERT(pointer);
305 androidPointer* ptr = (androidPointer*)pointer;
309 const jsize nPixels = (jsize)(pointer->width * pointer->height);
311 JNIEnv* env =
nullptr;
312 jboolean attached = jni_attach_thread(&env);
314 if (!gJavaActivityClass || !gOnPointerSetMethod)
317 jintArray pixels = (*env)->NewIntArray(env, nPixels);
321 (*env)->SetIntArrayRegion(env, pixels, 0, nPixels, (
const jint*)ptr->data);
322 (*env)->CallStaticVoidMethod(env, gJavaActivityClass, gOnPointerSetMethod,
323 (jlong)context->instance, pixels, (jint)pointer->width,
324 (jint)pointer->height, (jint)pointer->xPos, (jint)pointer->yPos);
325 (*env)->DeleteLocalRef(env, pixels);
333static BOOL android_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
335 WINPR_ASSERT(context);
340static BOOL android_Pointer_SetNull(rdpContext* context)
342 WINPR_ASSERT(context);
344 freerdp_callback(
"OnPointerSetNull",
"(J)V", (jlong)context->instance);
348static BOOL android_Pointer_SetDefault(rdpContext* context)
350 WINPR_ASSERT(context);
352 freerdp_callback(
"OnPointerSetDefault",
"(J)V", (jlong)context->instance);
356static UINT android_UpdateWindowFromSurface(RdpgfxClientContext* context, gdiGfxSurface* surface)
358 if (!context || !surface)
359 return CHANNEL_RC_OK;
361 rdpGdi* gdi = (rdpGdi*)context->custom;
362 if (!gdi || !gdi->context)
363 return CHANNEL_RC_OK;
365 const UINT32 width = surface->mappedWidth ? surface->mappedWidth : surface->width;
366 const UINT32 height = surface->mappedHeight ? surface->mappedHeight : surface->height;
367 if (width == 0 || height == 0)
368 return CHANNEL_RC_OK;
370 JNIEnv* env =
nullptr;
371 jboolean attached = jni_attach_thread(&env);
372 if (!gJavaActivityClass || !gOnRailWindowUpdateMethod)
375 const jsize nPixels = (jsize)(width * height);
376 jintArray pixels = (*env)->NewIntArray(env, nPixels);
380 jint* dst = (*env)->GetIntArrayElements(env, pixels,
nullptr);
383 freerdp_image_copy((BYTE*)dst, surface->format, width * 4, 0, 0, width, height,
384 surface->data, surface->format, surface->scanline, 0, 0,
nullptr,
389 const size_t total = (size_t)width * height;
390 for (
size_t i = 0; i < total; i++)
392 const UINT32 px = (UINT32)dst[i];
393 if ((px & 0x00FFFFFFu) != 0)
394 dst[i] = (jint)(px | 0xFF000000u);
396 (*env)->ReleaseIntArrayElements(env, pixels, dst, 0);
399 freerdp* inst = gdi->context->instance;
400 (*env)->CallStaticVoidMethod(env, gJavaActivityClass, gOnRailWindowUpdateMethod, (jlong)inst,
401 (jlong)surface->windowId, (jint)width, (jint)height, pixels);
402 (*env)->DeleteLocalRef(env, pixels);
406 return CHANNEL_RC_OK;
409static BOOL android_register_pointer(rdpGraphics* graphics)
411 rdpPointer pointer = WINPR_C_ARRAY_INIT;
416 pointer.size =
sizeof(androidPointer);
417 pointer.New = android_Pointer_New;
418 pointer.Free = android_Pointer_Free;
419 pointer.Set = android_Pointer_Set;
420 pointer.SetNull = android_Pointer_SetNull;
421 pointer.SetDefault = android_Pointer_SetDefault;
422 pointer.SetPosition = android_Pointer_SetPosition;
423 graphics_register_pointer(graphics, &pointer);
428#define ANDROID_EXPERIMENTAL_REMOTEAPP 0
429#define ANDROID_EXPERIMENTAL_CAMERA 1
431static BOOL android_post_connect(freerdp* instance)
433 WINPR_ASSERT(instance);
434 WINPR_ASSERT(instance->context);
436 rdpUpdate* update = instance->context->update;
437 WINPR_ASSERT(update);
439 rdpSettings* settings = instance->context->settings;
440 WINPR_ASSERT(settings);
443 !freerdp_callback_bool_result(
"OnExperimentalFeature",
"(JI)Z", (jlong)instance,
444 ANDROID_EXPERIMENTAL_REMOTEAPP))
447 if (freerdp_dynamic_channel_collection_find(settings,
"rdpecam") &&
448 !freerdp_callback_bool_result(
"OnExperimentalFeature",
"(JI)Z", (jlong)instance,
449 ANDROID_EXPERIMENTAL_CAMERA))
452 if (!gdi_init(instance, PIXEL_FORMAT_RGBX32))
455 if (!android_register_pointer(instance->context->graphics))
458 update->BeginPaint = android_begin_paint;
459 update->EndPaint = android_end_paint;
460 update->DesktopResize = android_desktop_resize;
461 freerdp_callback(
"OnSettingsChanged",
"(JIII)V", (jlong)instance,
465 freerdp_callback(
"OnConnectionSuccess",
"(J)V", (jlong)instance);
469static void android_post_disconnect(freerdp* instance)
471 freerdp_callback(
"OnDisconnecting",
"(J)V", (jlong)instance);
475static void android_post_final_disconnect(freerdp* instance)
477 WINPR_ASSERT(instance);
478 WINPR_ASSERT(instance->context);
480 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
481 android_OnChannelConnectedEventHandler);
482 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
483 android_OnChannelDisconnectedEventHandler);
486static BOOL android_authenticate_int(freerdp* instance,
char** username,
char** password,
487 char** domain,
const char* cb_name)
490 jboolean attached = jni_attach_thread(&env);
491 jobject jstr1 = create_string_builder(env, *username);
492 jobject jstr2 = create_string_builder(env, *domain);
493 jobject jstr3 = create_string_builder(env, *password);
495 res = freerdp_callback_bool_result(cb_name,
496 "(JLjava/lang/StringBuilder;"
497 "Ljava/lang/StringBuilder;"
498 "Ljava/lang/StringBuilder;)Z",
499 (jlong)instance, jstr1, jstr2, jstr3);
505 *username = get_string_from_string_builder(env, jstr1);
507 *domain = get_string_from_string_builder(env, jstr2);
509 *password = get_string_from_string_builder(env, jstr3);
512 if (attached == JNI_TRUE)
515 return ((res == JNI_TRUE) ? TRUE : FALSE);
518static BOOL android_authenticate_ex(freerdp* instance,
char** username,
char** password,
519 char** domain, rdp_auth_reason reason)
526 return android_authenticate_int(instance, username, password, domain,
"OnAuthenticate");
530 return android_authenticate_int(instance, username, password, domain,
531 "OnGatewayAuthenticate");
537static DWORD android_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
538 const char* common_name,
const char* subject,
539 const char* issuer,
const char* fingerprint, DWORD flags)
541 WLog_DBG(TAG,
"Certificate details [%s:%" PRIu16
":", host, port);
542 WLog_DBG(TAG,
"\tSubject: %s", subject);
543 WLog_DBG(TAG,
"\tIssuer: %s", issuer);
544 WLog_DBG(TAG,
"\tThumbprint: %s", fingerprint);
546 "The above X.509 certificate could not be verified, possibly because you do not have "
547 "the CA certificate in your certificate store, or the certificate has expired."
548 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
550 jboolean attached = jni_attach_thread(&env);
551 jstring jstr0 = (*env)->NewStringUTF(env, host);
552 jstring jstr1 = (*env)->NewStringUTF(env, common_name);
553 jstring jstr2 = (*env)->NewStringUTF(env, subject);
554 jstring jstr3 = (*env)->NewStringUTF(env, issuer);
555 jstring jstr4 = (*env)->NewStringUTF(env, fingerprint);
556 jint res = freerdp_callback_int_result(
"OnVerifyCertificateEx",
557 "(JLjava/lang/String;JLjava/lang/String;Ljava/lang/"
558 "String;Ljava/lang/String;Ljava/lang/String;J)I",
559 (jlong)instance, jstr0, (jlong)port, jstr1, jstr2, jstr3,
560 jstr4, (jlong)flags);
562 if (attached == JNI_TRUE)
568static DWORD android_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
569 const char* common_name,
const char* subject,
570 const char* issuer,
const char* new_fingerprint,
571 const char* old_subject,
const char* old_issuer,
572 const char* old_fingerprint, DWORD flags)
575 jboolean attached = jni_attach_thread(&env);
576 jstring jhost = (*env)->NewStringUTF(env, host);
577 jstring jstr0 = (*env)->NewStringUTF(env, common_name);
578 jstring jstr1 = (*env)->NewStringUTF(env, subject);
579 jstring jstr2 = (*env)->NewStringUTF(env, issuer);
580 jstring jstr3 = (*env)->NewStringUTF(env, new_fingerprint);
581 jstring jstr4 = (*env)->NewStringUTF(env, old_subject);
582 jstring jstr5 = (*env)->NewStringUTF(env, old_issuer);
583 jstring jstr6 = (*env)->NewStringUTF(env, old_fingerprint);
585 freerdp_callback_int_result(
"OnVerifyChangedCertificateEx",
586 "(JLjava/lang/String;JLjava/lang/String;Ljava/lang/"
587 "String;Ljava/lang/String;Ljava/lang/String;"
588 "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)I",
589 (jlong)instance, jhost, (jlong)port, jstr0, jstr1, jstr2, jstr3,
590 jstr4, jstr5, jstr6, (jlong)flags);
592 if (attached == JNI_TRUE)
598static int android_freerdp_run(freerdp* instance)
600 WINPR_ASSERT(instance);
602 DWORD status = WAIT_FAILED;
603 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
604 HANDLE inputEvent =
nullptr;
605 rdpContext* context = instance->context;
606 WINPR_ASSERT(context);
608 const rdpSettings* settings = context->settings;
610 inputEvent = android_get_handle(instance);
612 while (!freerdp_shall_disconnect_context(context))
616 handles[count++] = inputEvent;
618 DWORD tmp = freerdp_get_event_handles(context, &handles[count], 64 - count);
622 WLog_ERR(TAG,
"freerdp_get_event_handles failed");
627 status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
629 if (status == WAIT_FAILED)
631 WLog_ERR(TAG,
"WaitForMultipleObjects failed with %u [%08X]", status,
632 (
unsigned)GetLastError());
636 if (!freerdp_check_event_handles(context))
638 if (!client_auto_reconnect(instance))
640 WLog_ERR(TAG,
"Failed to check FreeRDP file descriptor");
641 status = GetLastError();
646 if (freerdp_shall_disconnect_context(instance->context))
649 if (android_check_handle(instance) != TRUE)
651 WLog_ERR(TAG,
"Failed to check android file descriptor");
652 status = GetLastError();
658 WLog_INFO(TAG,
"Prepare shutdown...");
663static DWORD WINAPI android_thread_func(LPVOID param)
665 DWORD status = ERROR_BAD_ARGUMENTS;
666 freerdp* instance = param;
667 WLog_DBG(TAG,
"Start...");
669 WINPR_ASSERT(instance);
670 WINPR_ASSERT(instance->context);
672 if (freerdp_client_start(instance->context) != CHANNEL_RC_OK)
675 WLog_DBG(TAG,
"Connect...");
677 if (!freerdp_connect(instance))
678 status = GetLastError();
681 status = android_freerdp_run(instance);
682 WLog_DBG(TAG,
"Disconnect...");
684 if (!freerdp_disconnect(instance))
685 status = GetLastError();
688 WLog_DBG(TAG,
"Stop...");
690 if (freerdp_client_stop(instance->context) != CHANNEL_RC_OK)
694 WLog_DBG(TAG,
"Session ended with %08" PRIX32
"", status);
696 if (status == CHANNEL_RC_OK)
697 freerdp_callback(
"OnDisconnected",
"(J)V", (jlong)instance);
699 freerdp_callback(
"OnConnectionFailure",
"(J)V", (jlong)instance);
701 WLog_DBG(TAG,
"Quit.");
706static BOOL android_client_new(freerdp* instance, rdpContext* context)
708 WINPR_ASSERT(instance);
709 WINPR_ASSERT(context);
711 if (!android_event_queue_init(instance))
714 instance->PreConnect = android_pre_connect;
715 instance->PostConnect = android_post_connect;
716 instance->PostDisconnect = android_post_disconnect;
717 instance->PostFinalDisconnect = android_post_final_disconnect;
718 instance->AuthenticateEx = android_authenticate_ex;
719 instance->VerifyCertificateEx = android_verify_certificate_ex;
720 instance->VerifyChangedCertificateEx = android_verify_changed_certificate_ex;
721 instance->LogonErrorInfo =
nullptr;
725static void android_client_free(freerdp* instance, rdpContext* context)
730 android_event_queue_uninit(instance);
733static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
735 WINPR_ASSERT(pEntryPoints);
737 ZeroMemory(pEntryPoints,
sizeof(RDP_CLIENT_ENTRY_POINTS));
739 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
740 pEntryPoints->Size =
sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
741 pEntryPoints->GlobalInit =
nullptr;
742 pEntryPoints->GlobalUninit =
nullptr;
744 pEntryPoints->ClientNew = android_client_new;
745 pEntryPoints->ClientFree = android_client_free;
746 pEntryPoints->ClientStart =
nullptr;
747 pEntryPoints->ClientStop =
nullptr;
751JNIEXPORT jlong JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1new(
752 JNIEnv* env, jclass cls, jobject context)
757 jmethodID getFilesDirID;
758 jmethodID getAbsolutePathID;
762 RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
764#if defined(WITH_GPROF)
765 setenv(
"CPUPROFILE_FREQUENCY",
"200", 1);
766 monstartup(
"libfreerdp-android.so");
768 contextClass = (*env)->FindClass(env, JAVA_CONTEXT_CLASS);
769 fileClass = (*env)->FindClass(env, JAVA_FILE_CLASS);
771 if (!contextClass || !fileClass)
773 WLog_FATAL(TAG,
"Failed to load class references %s=%p, %s=%p", JAVA_CONTEXT_CLASS,
774 (
void*)contextClass, JAVA_FILE_CLASS, (
void*)fileClass);
775 return (jlong)
nullptr;
779 (*env)->GetMethodID(env, contextClass,
"getFilesDir",
"()L" JAVA_FILE_CLASS
";");
783 WLog_FATAL(TAG,
"Failed to find method ID getFilesDir ()L" JAVA_FILE_CLASS
";");
784 return (jlong)
nullptr;
788 (*env)->GetMethodID(env, fileClass,
"getAbsolutePath",
"()Ljava/lang/String;");
790 if (!getAbsolutePathID)
792 WLog_FATAL(TAG,
"Failed to find method ID getAbsolutePath ()Ljava/lang/String;");
793 return (jlong)
nullptr;
796 filesDirObj = (*env)->CallObjectMethod(env, context, getFilesDirID);
800 WLog_FATAL(TAG,
"Failed to call getFilesDir");
801 return (jlong)
nullptr;
804 path = (*env)->CallObjectMethod(env, filesDirObj, getAbsolutePathID);
808 WLog_FATAL(TAG,
"Failed to call getAbsolutePath");
809 return (jlong)
nullptr;
812 raw = (*env)->GetStringUTFChars(env, path, 0);
816 WLog_FATAL(TAG,
"Failed to get C string from java string");
817 return (jlong)
nullptr;
820 envStr = _strdup(raw);
821 (*env)->ReleaseStringUTFChars(env, path, raw);
825 WLog_FATAL(TAG,
"_strdup(%s) failed", raw);
826 return (jlong)
nullptr;
829 if (setenv(
"HOME", _strdup(envStr), 1) != 0)
831 char ebuffer[256] = WINPR_C_ARRAY_INIT;
832 WLog_FATAL(TAG,
"Failed to set environment HOME=%s %s [%d]", envStr,
833 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
834 return (jlong)
nullptr;
837 RdpClientEntry(&clientEntryPoints);
838 ctx = freerdp_client_context_new(&clientEntryPoints);
841 return (jlong)
nullptr;
843 return (jlong)ctx->instance;
846JNIEXPORT
void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1free(
847 JNIEnv* env, jclass cls, jlong instance)
849 freerdp* inst = (freerdp*)instance;
852 freerdp_client_context_free(inst->context);
854#if defined(WITH_GPROF)
859JNIEXPORT jstring JNICALL
860Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1last_1error_1string(JNIEnv* env,
864 freerdp* inst = (freerdp*)instance;
866 if (!inst || !inst->context)
867 return (*env)->NewStringUTF(env,
"");
869 return (*env)->NewStringUTF(
870 env, freerdp_get_last_error_string(freerdp_get_last_error(inst->context)));
873JNIEXPORT jboolean JNICALL
874Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1parse_1arguments(JNIEnv* env, jclass cls,
876 jobjectArray arguments)
878 freerdp* inst = (freerdp*)instance;
883 if (!inst || !inst->context)
886 count = (*env)->GetArrayLength(env, arguments);
887 argv = calloc(count,
sizeof(
char*));
892 for (
int i = 0; i < count; i++)
894 jstring str = (jstring)(*env)->GetObjectArrayElement(env, arguments, i);
895 const char* raw = (*env)->GetStringUTFChars(env, str, 0);
896 argv[i] = _strdup(raw);
897 (*env)->ReleaseStringUTFChars(env, str, raw);
901 freerdp_client_settings_parse_command_line(inst->context->settings, count, argv, FALSE);
903 for (
int i = 0; i < count; i++)
907 return (status == 0) ? JNI_TRUE : JNI_FALSE;
910JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1connect(
911 JNIEnv* env, jclass cls, jlong instance)
913 freerdp* inst = (freerdp*)instance;
915 if (!inst || !inst->context)
917 WLog_FATAL(TAG,
"(env=%p, cls=%p, instance=%" PRId64, (
void*)env, (
void*)cls,
924 if (!(ctx->thread = CreateThread(
nullptr, 0, android_thread_func, inst, 0,
nullptr)))
932JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1disconnect(
933 JNIEnv* env, jclass cls, jlong instance)
935 freerdp* inst = (freerdp*)instance;
937 if (!inst || !inst->context || !cls || !env)
939 WLog_FATAL(TAG,
"(env=%p, cls=%p, instance=%" PRId64, (
void*)env, (
void*)cls,
950 if (!android_push_event(inst, event))
956 if (!freerdp_abort_connect_context(inst->context))
962JNIEXPORT jboolean JNICALL
963Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1update_1graphics(JNIEnv* env, jclass cls,
965 jobject bitmap, jint x,
973 AndroidBitmapInfo info;
974 freerdp* inst = (freerdp*)instance;
977 if (!env || !cls || !inst)
979 WLog_FATAL(TAG,
"(env=%p, cls=%p, instance=%" PRId64, (
void*)env, (
void*)cls,
984 gdi = inst->context->gdi;
986 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0)
988 WLog_FATAL(TAG,
"AndroidBitmap_getInfo() failed ! error=%d", ret);
992 if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0)
994 WLog_FATAL(TAG,
"AndroidBitmap_lockPixels() failed ! error=%d", ret);
1000 switch (info.format)
1002 case ANDROID_BITMAP_FORMAT_RGBA_8888:
1003 DstFormat = PIXEL_FORMAT_RGBX32;
1006 case ANDROID_BITMAP_FORMAT_RGB_565:
1007 DstFormat = PIXEL_FORMAT_RGB16;
1010 case ANDROID_BITMAP_FORMAT_RGBA_4444:
1011 case ANDROID_BITMAP_FORMAT_A_8:
1012 case ANDROID_BITMAP_FORMAT_NONE:
1020 rc = freerdp_image_copy(pixels, DstFormat, info.stride, x, y, width, height,
1021 gdi->primary_buffer, gdi->dstFormat, gdi->stride, x, y,
1022 &gdi->palette, FREERDP_FLIP_NONE);
1025 if ((ret = AndroidBitmap_unlockPixels(env, bitmap)) < 0)
1027 WLog_FATAL(TAG,
"AndroidBitmap_unlockPixels() failed ! error=%d", ret);
1034JNIEXPORT jboolean JNICALL
1035Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1key_1event(JNIEnv* env, jclass cls,
1042 freerdp* inst = (freerdp*)instance;
1043 scancode = GetVirtualScanCodeFromVirtualKeyCode(keycode, 4);
1044 int flags = (down == JNI_TRUE) ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE;
1045 flags |= (scancode & KBDEXT) ? KBD_FLAGS_EXTENDED : 0;
1046 event = (
ANDROID_EVENT*)android_event_key_new(flags, scancode & 0xFF);
1051 if (!android_push_event(inst, event))
1053 android_event_free(event);
1057 WLog_DBG(TAG,
"send_key_event: %" PRIu32
", %d", scancode, flags);
1061JNIEXPORT jboolean JNICALL
1062Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1unicodekey_1event(
1063 JNIEnv* env, jclass cls, jlong instance, jint keycode, jboolean down)
1066 freerdp* inst = (freerdp*)instance;
1067 UINT16 flags = (down == JNI_TRUE) ? 0 : KBD_FLAGS_RELEASE;
1068 event = (
ANDROID_EVENT*)android_event_unicodekey_new(flags, keycode);
1073 if (!android_push_event(inst, event))
1075 android_event_free(event);
1079 WLog_DBG(TAG,
"send_unicodekey_event: %d", keycode);
1083JNIEXPORT jboolean JNICALL
1084Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1is_1unicode_1input_1supported(
1085 JNIEnv* env, jclass cls, jlong instance)
1087 freerdp* inst = (freerdp*)instance;
1089 if (!inst || !inst->context || !inst->context->settings)
1098JNIEXPORT jboolean JNICALL
1099Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1cursor_1event(
1100 JNIEnv* env, jclass cls, jlong instance, jint x, jint y, jint flags)
1103 freerdp* inst = (freerdp*)instance;
1104 event = (
ANDROID_EVENT*)android_event_cursor_new(flags, x, y);
1109 if (!android_push_event(inst, event))
1111 android_event_free(event);
1115 WLog_DBG(TAG,
"send_cursor_event: (%d, %d), %d", x, y, flags);
1119static jboolean android_push_clipboard_event(freerdp* inst,
const void* data,
size_t data_length,
1120 const char* mimeType)
1125 if (!android_push_event(inst, event))
1127 android_event_free(event);
1133JNIEXPORT jboolean JNICALL
1134Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1clipboard_1data(JNIEnv* env,
1140 freerdp* inst = (freerdp*)instance;
1141 const char* data = jdata !=
nullptr ? (*env)->GetStringUTFChars(env, jdata,
nullptr) :
nullptr;
1142 const size_t data_length = data ? (*env)->GetStringUTFLength(env, jdata) : 0;
1143 jboolean ret = android_push_clipboard_event(inst, data, data_length,
"text/plain");
1144 WLog_DBG(TAG,
"send_clipboard_data: (%s)", data);
1147 (*env)->ReleaseStringUTFChars(env, jdata, data);
1152static BOOL android_is_image_mime_supported(
const char* mimeType)
1156 if (strcmp(mimeType,
"image/png") == 0)
1157 return winpr_image_format_is_supported(WINPR_IMAGE_PNG);
1158 if (strcmp(mimeType,
"image/jpeg") == 0 || strcmp(mimeType,
"image/jpg") == 0)
1159 return winpr_image_format_is_supported(WINPR_IMAGE_JPEG);
1160 if (strcmp(mimeType,
"image/webp") == 0)
1161 return winpr_image_format_is_supported(WINPR_IMAGE_WEBP);
1165JNIEXPORT jboolean JNICALL
1166Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1clipboard_1image_1data(
1167 JNIEnv* env, jclass cls, jlong instance, jbyteArray jdata, jstring jmimeType)
1170 freerdp* inst = (freerdp*)instance;
1171 jsize data_length = (*env)->GetArrayLength(env, jdata);
1172 jbyte* data = (*env)->GetByteArrayElements(env, jdata,
nullptr);
1173 const char* mimeType = jmimeType ? (*env)->GetStringUTFChars(env, jmimeType,
nullptr) :
nullptr;
1174 jboolean ret = JNI_FALSE;
1175 if (android_is_image_mime_supported(mimeType))
1176 ret = android_push_clipboard_event(inst, data, (
size_t)data_length, mimeType);
1179 (*env)->ReleaseStringUTFChars(env, jmimeType, mimeType);
1180 (*env)->ReleaseByteArrayElements(env, jdata, data, JNI_ABORT);
1184JNIEXPORT jboolean JNICALL
1185Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1monitor_1layout(
1186 JNIEnv* env, jclass cls, jlong instance, jint width, jint height)
1190 freerdp* inst = (freerdp*)instance;
1192 if (!inst || !inst->context)
1196 return android_disp_send_monitor_layout(afc, (UINT32)width, (UINT32)height) ? JNI_TRUE
1200JNIEXPORT jstring JNICALL
1201Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1jni_1version(JNIEnv* env, jclass cls)
1203 return (*env)->NewStringUTF(env, FREERDP_JNI_VERSION);
1206JNIEXPORT jboolean JNICALL
1207Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1has_1h264(JNIEnv* env, jclass cls)
1209 H264_CONTEXT* ctx = h264_context_new(FALSE);
1212 h264_context_free(ctx);
1216JNIEXPORT jboolean JNICALL
1217Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1has_1camera_1redirection(JNIEnv* env,
1220 return freerdp_video_conversion_supported(FREERDP_VIDEO_FORMAT_NV12,
1221 FREERDP_VIDEO_FORMAT_YUV420P)
1226JNIEXPORT jstring JNICALL
1227Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1version(JNIEnv* env, jclass cls)
1229 return (*env)->NewStringUTF(env, freerdp_get_version_string());
1232JNIEXPORT jstring JNICALL
1233Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1build_1revision(JNIEnv* env,
1236 return (*env)->NewStringUTF(env, freerdp_get_build_revision());
1239JNIEXPORT jstring JNICALL
1240Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1build_1config(JNIEnv* env,
1243 return (*env)->NewStringUTF(env, freerdp_get_build_config());
1246jint JNI_OnLoad(JavaVM* vm,
void* reserved)
1249 setlocale(LC_ALL,
"");
1250 WLog_DBG(TAG,
"Setting up JNI environment...");
1259 if ((*vm)->GetEnv(vm, (
void**)&env, JNI_VERSION_1_6) != JNI_OK)
1261 WLog_FATAL(TAG,
"Failed to get the environment");
1266 jclass activityClass = (*env)->FindClass(env, JAVA_LIBFREERDP_CLASS);
1270 WLog_FATAL(TAG,
"failed to get %s class reference", JAVA_LIBFREERDP_CLASS);
1275 gJavaActivityClass = (*env)->NewGlobalRef(env, activityClass);
1276 gOnPointerSetMethod =
1277 (*env)->GetStaticMethodID(env, gJavaActivityClass,
"OnPointerSet",
"(J[IIIII)V");
1278 gOnRailWindowUpdateMethod =
1279 (*env)->GetStaticMethodID(env, gJavaActivityClass,
"OnRailWindowUpdate",
"(JJII[I)V");
1280 if (!gOnRailWindowUpdateMethod)
1282 (*env)->ExceptionClear(env);
1283 WLog_WARN(TAG,
"OnRailWindowUpdate method not found, RAIL window display disabled");
1286 return init_callback_environment(vm, env);
1289void JNICALL JNI_OnUnload(JavaVM* vm,
void* reserved)
1292 WLog_DBG(TAG,
"Tearing down JNI environment...");
1294 if ((*vm)->GetEnv(vm, (
void**)&env, JNI_VERSION_1_6) != JNI_OK)
1296 WLog_FATAL(TAG,
"Failed to get the environment");
1300 if (gJavaActivityClass)
1301 (*env)->DeleteGlobalRef(env, gJavaActivityClass);
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.