FreeRDP
Loading...
Searching...
No Matches
android_freerdp.c
1/*
2 Android JNI Client Layer
3
4 Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
5 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
6 Copyright 2013 Thincast Technologies GmbH, Author: Armin Novak
7 Copyright 2015 Bernhard Miklautz <bernhard.miklautz@thincast.com>
8 Copyright 2016 Thincast Technologies GmbH
9 Copyright 2016 Armin Novak <armin.novak@thincast.com>
10
11 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
12 If a copy of the MPL was not distributed with this file, You can obtain one at
13 http://mozilla.org/MPL/2.0/.
14*/
15
16#include <freerdp/config.h>
17
18#include <locale.h>
19
20#include <jni.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <errno.h>
24
25#include <winpr/assert.h>
26#include <winpr/image.h>
27
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/channels/channels.h>
37#include <freerdp/client/channels.h>
38#include <freerdp/client/cmdline.h>
39#include <freerdp/constants.h>
40#include <freerdp/locale/keyboard.h>
41#include <freerdp/primitives.h>
42#include <freerdp/version.h>
43#include <freerdp/settings.h>
44#include <freerdp/utils/signal.h>
45
46#include <android/bitmap.h>
47
48#include "android_jni_callback.h"
49#include "android_jni_utils.h"
50#include "android_cliprdr.h"
51#include "android_disp.h"
52#include "android_freerdp_jni.h"
53
54#if defined(WITH_GPROF)
55#include "jni/prof.h"
56#endif
57
58#define TAG CLIENT_TAG("android")
59
60/* Defines the JNI version supported by this library. */
61#define FREERDP_JNI_VERSION FREERDP_VERSION_FULL
62
63static jclass gJavaActivityClass;
64static jmethodID gOnPointerSetMethod;
65static void android_OnChannelConnectedEventHandler(void* context,
66 const ChannelConnectedEventArgs* e)
67{
68 rdpSettings* settings;
69 androidContext* afc;
70
71 if (!context || !e)
72 {
73 WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void*)e);
74 return;
75 }
76
77 afc = (androidContext*)context;
78 settings = afc->common.context.settings;
79
80 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
81 {
82 android_cliprdr_init(afc, (CliprdrClientContext*)e->pInterface);
83 }
84 else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
85 {
86 android_disp_init(afc, (DispClientContext*)e->pInterface);
87 }
88 else
89 freerdp_client_OnChannelConnectedEventHandler(context, e);
90}
91
92static void android_OnChannelDisconnectedEventHandler(void* context,
93 const ChannelDisconnectedEventArgs* e)
94{
95 rdpSettings* settings;
96 androidContext* afc;
97
98 if (!context || !e)
99 {
100 WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void*)e);
101 return;
102 }
103
104 afc = (androidContext*)context;
105 settings = afc->common.context.settings;
106
107 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
108 {
109 android_cliprdr_uninit(afc, (CliprdrClientContext*)e->pInterface);
110 }
111 else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
112 {
113 android_disp_uninit(afc, (DispClientContext*)e->pInterface);
114 }
115 else
116 freerdp_client_OnChannelDisconnectedEventHandler(context, e);
117}
118
119static BOOL android_begin_paint(rdpContext* context)
120{
121 return TRUE;
122}
123
124static BOOL android_end_paint(rdpContext* context)
125{
126 HGDI_WND hwnd;
127 int ninvalid;
128 rdpGdi* gdi;
129 HGDI_RGN cinvalid;
130 int x1, y1, x2, y2;
131 androidContext* ctx = (androidContext*)context;
132 rdpSettings* settings;
133
134 if (!ctx || !context->instance)
135 return FALSE;
136
137 settings = context->settings;
138
139 if (!settings)
140 return FALSE;
141
142 gdi = context->gdi;
143
144 if (!gdi || !gdi->primary || !gdi->primary->hdc)
145 return FALSE;
146
147 hwnd = ctx->common.context.gdi->primary->hdc->hwnd;
148
149 if (!hwnd)
150 return FALSE;
151
152 ninvalid = hwnd->ninvalid;
153
154 if (ninvalid < 1)
155 return TRUE;
156
157 cinvalid = hwnd->cinvalid;
158
159 if (!cinvalid)
160 return FALSE;
161
162 x1 = cinvalid[0].x;
163 y1 = cinvalid[0].y;
164 x2 = cinvalid[0].x + cinvalid[0].w;
165 y2 = cinvalid[0].y + cinvalid[0].h;
166
167 for (int i = 0; i < ninvalid; i++)
168 {
169 x1 = MIN(x1, cinvalid[i].x);
170 y1 = MIN(y1, cinvalid[i].y);
171 x2 = MAX(x2, cinvalid[i].x + cinvalid[i].w);
172 y2 = MAX(y2, cinvalid[i].y + cinvalid[i].h);
173 }
174
175 freerdp_callback("OnGraphicsUpdate", "(JIIII)V", (jlong)context->instance, x1, y1, x2 - x1,
176 y2 - y1);
177
178 hwnd->invalid->null = TRUE;
179 hwnd->ninvalid = 0;
180 return TRUE;
181}
182
183static BOOL android_desktop_resize(rdpContext* context)
184{
185 WINPR_ASSERT(context);
186 WINPR_ASSERT(context->settings);
187 WINPR_ASSERT(context->instance);
188
189 const UINT32 width = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
190 const UINT32 height = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopHeight);
191
192 if (context->gdi && !gdi_resize(context->gdi, width, height))
193 return FALSE;
194
195 freerdp_callback("OnGraphicsResize", "(JIII)V", (jlong)context->instance, width, height,
196 freerdp_settings_get_uint32(context->settings, FreeRDP_ColorDepth));
197 return TRUE;
198}
199
200static BOOL android_pre_connect(freerdp* instance)
201{
202 WINPR_ASSERT(instance);
203 WINPR_ASSERT(instance->context);
204
205 rdpSettings* settings = instance->context->settings;
206
207 if (!settings)
208 return FALSE;
209
210 int rc = PubSub_SubscribeChannelConnected(instance->context->pubSub,
211 android_OnChannelConnectedEventHandler);
212
213 if (rc != CHANNEL_RC_OK)
214 {
215 WLog_ERR(TAG, "Could not subscribe to connect event handler [%08X]", rc);
216 return FALSE;
217 }
218
219 rc = PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
220 android_OnChannelDisconnectedEventHandler);
221
222 if (rc != CHANNEL_RC_OK)
223 {
224 WLog_ERR(TAG, "Could not subscribe to disconnect event handler [%08X]", rc);
225 return FALSE;
226 }
227
228 freerdp_callback("OnPreConnect", "(J)V", (jlong)instance);
229 return TRUE;
230}
231
232typedef struct
233{
234 rdpPointer pointer;
235 size_t size;
236 void* data;
237} androidPointer;
238
239static BOOL android_Pointer_New(rdpContext* context, rdpPointer* pointer)
240{
241 WINPR_ASSERT(context);
242 WINPR_ASSERT(pointer);
243 WINPR_ASSERT(context->gdi);
244
245 androidPointer* ptr = (androidPointer*)pointer;
246 if (!ptr)
247 return FALSE;
248
249 ptr->size = 4ULL * pointer->width * pointer->height;
250 ptr->data = winpr_aligned_malloc(ptr->size, 16);
251 if (!ptr->data)
252 return FALSE;
253
254 if (!freerdp_image_copy_from_pointer_data(
255 ptr->data, PIXEL_FORMAT_BGRA32, 0, 0, 0, pointer->width, pointer->height,
256 pointer->xorMaskData, pointer->lengthXorMask, pointer->andMaskData,
257 pointer->lengthAndMask, pointer->xorBpp, &context->gdi->palette))
258 {
259 winpr_aligned_free(ptr->data);
260 ptr->data = nullptr;
261 return FALSE;
262 }
263
264 return TRUE;
265}
266
267static void android_Pointer_Free(rdpContext* context, rdpPointer* pointer)
268{
269 WINPR_UNUSED(context);
270 androidPointer* ptr = (androidPointer*)pointer;
271
272 if (ptr)
273 {
274 winpr_aligned_free(ptr->data);
275 ptr->data = nullptr;
276 }
277}
278
279static BOOL android_Pointer_Set(rdpContext* context, rdpPointer* pointer)
280{
281 WINPR_ASSERT(context);
282 WINPR_ASSERT(pointer);
283
284 androidPointer* ptr = (androidPointer*)pointer;
285 if (!ptr->data)
286 return FALSE;
287
288 const jsize nPixels = (jsize)(pointer->width * pointer->height);
289
290 JNIEnv* env = NULL;
291 jboolean attached = jni_attach_thread(&env);
292
293 if (!gJavaActivityClass || !gOnPointerSetMethod)
294 goto done;
295
296 jintArray pixels = (*env)->NewIntArray(env, nPixels);
297 if (!pixels)
298 goto done;
299
300 (*env)->SetIntArrayRegion(env, pixels, 0, nPixels, (const jint*)ptr->data);
301 (*env)->CallStaticVoidMethod(env, gJavaActivityClass, gOnPointerSetMethod,
302 (jlong)context->instance, pixels, (jint)pointer->width,
303 (jint)pointer->height, (jint)pointer->xPos, (jint)pointer->yPos);
304 (*env)->DeleteLocalRef(env, pixels);
305done:
306 if (attached)
307 jni_detach_thread();
308
309 return TRUE;
310}
311
312static BOOL android_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
313{
314 WINPR_ASSERT(context);
315
316 return TRUE;
317}
318
319static BOOL android_Pointer_SetNull(rdpContext* context)
320{
321 WINPR_ASSERT(context);
322
323 freerdp_callback("OnPointerSetNull", "(J)V", (jlong)context->instance);
324 return TRUE;
325}
326
327static BOOL android_Pointer_SetDefault(rdpContext* context)
328{
329 WINPR_ASSERT(context);
330
331 freerdp_callback("OnPointerSetDefault", "(J)V", (jlong)context->instance);
332 return TRUE;
333}
334
335static BOOL android_register_pointer(rdpGraphics* graphics)
336{
337 rdpPointer pointer = WINPR_C_ARRAY_INIT;
338
339 if (!graphics)
340 return FALSE;
341
342 pointer.size = sizeof(androidPointer);
343 pointer.New = android_Pointer_New;
344 pointer.Free = android_Pointer_Free;
345 pointer.Set = android_Pointer_Set;
346 pointer.SetNull = android_Pointer_SetNull;
347 pointer.SetDefault = android_Pointer_SetDefault;
348 pointer.SetPosition = android_Pointer_SetPosition;
349 graphics_register_pointer(graphics, &pointer);
350 return TRUE;
351}
352
353static BOOL android_post_connect(freerdp* instance)
354{
355 rdpSettings* settings;
356 rdpUpdate* update;
357
358 WINPR_ASSERT(instance);
359 WINPR_ASSERT(instance->context);
360
361 update = instance->context->update;
362 WINPR_ASSERT(update);
363
364 settings = instance->context->settings;
365 WINPR_ASSERT(settings);
366
367 if (!gdi_init(instance, PIXEL_FORMAT_RGBX32))
368 return FALSE;
369
370 if (!android_register_pointer(instance->context->graphics))
371 return FALSE;
372
373 update->BeginPaint = android_begin_paint;
374 update->EndPaint = android_end_paint;
375 update->DesktopResize = android_desktop_resize;
376 freerdp_callback("OnSettingsChanged", "(JIII)V", (jlong)instance,
377 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
378 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight),
379 freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth));
380 freerdp_callback("OnConnectionSuccess", "(J)V", (jlong)instance);
381 return TRUE;
382}
383
384static void android_post_disconnect(freerdp* instance)
385{
386 freerdp_callback("OnDisconnecting", "(J)V", (jlong)instance);
387 gdi_free(instance);
388}
389
390static BOOL android_authenticate_int(freerdp* instance, char** username, char** password,
391 char** domain, const char* cb_name)
392{
393 JNIEnv* env;
394 jboolean attached = jni_attach_thread(&env);
395 jobject jstr1 = create_string_builder(env, *username);
396 jobject jstr2 = create_string_builder(env, *domain);
397 jobject jstr3 = create_string_builder(env, *password);
398 jboolean res;
399 res = freerdp_callback_bool_result(cb_name,
400 "(JLjava/lang/StringBuilder;"
401 "Ljava/lang/StringBuilder;"
402 "Ljava/lang/StringBuilder;)Z",
403 (jlong)instance, jstr1, jstr2, jstr3);
404
405 if (res == JNI_TRUE)
406 {
407 // read back string values
408 free(*username);
409 *username = get_string_from_string_builder(env, jstr1);
410 free(*domain);
411 *domain = get_string_from_string_builder(env, jstr2);
412 free(*password);
413 *password = get_string_from_string_builder(env, jstr3);
414 }
415
416 if (attached == JNI_TRUE)
417 jni_detach_thread();
418
419 return ((res == JNI_TRUE) ? TRUE : FALSE);
420}
421
422static BOOL android_authenticate_ex(freerdp* instance, char** username, char** password,
423 char** domain, rdp_auth_reason reason)
424{
425 switch (reason)
426 {
427 case AUTH_NLA:
428 case AUTH_TLS:
429 case AUTH_RDP:
430 return android_authenticate_int(instance, username, password, domain, "OnAuthenticate");
431 case GW_AUTH_HTTP:
432 case GW_AUTH_RDG:
433 case GW_AUTH_RPC:
434 return android_authenticate_int(instance, username, password, domain,
435 "OnGatewayAuthenticate");
436 default:
437 return FALSE;
438 }
439}
440
441static DWORD android_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
442 const char* common_name, const char* subject,
443 const char* issuer, const char* fingerprint, DWORD flags)
444{
445 WLog_DBG(TAG, "Certificate details [%s:%" PRIu16 ":", host, port);
446 WLog_DBG(TAG, "\tSubject: %s", subject);
447 WLog_DBG(TAG, "\tIssuer: %s", issuer);
448 WLog_DBG(TAG, "\tThumbprint: %s", fingerprint);
449 WLog_DBG(TAG,
450 "The above X.509 certificate could not be verified, possibly because you do not have "
451 "the CA certificate in your certificate store, or the certificate has expired."
452 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
453 JNIEnv* env;
454 jboolean attached = jni_attach_thread(&env);
455 jstring jstr0 = (*env)->NewStringUTF(env, host);
456 jstring jstr1 = (*env)->NewStringUTF(env, common_name);
457 jstring jstr2 = (*env)->NewStringUTF(env, subject);
458 jstring jstr3 = (*env)->NewStringUTF(env, issuer);
459 jstring jstr4 = (*env)->NewStringUTF(env, fingerprint);
460 jint res = freerdp_callback_int_result("OnVerifyCertificateEx",
461 "(JLjava/lang/String;JLjava/lang/String;Ljava/lang/"
462 "String;Ljava/lang/String;Ljava/lang/String;J)I",
463 (jlong)instance, jstr0, (jlong)port, jstr1, jstr2, jstr3,
464 jstr4, (jlong)flags);
465
466 if (attached == JNI_TRUE)
467 jni_detach_thread();
468
469 return res;
470}
471
472static DWORD android_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
473 const char* common_name, const char* subject,
474 const char* issuer, const char* new_fingerprint,
475 const char* old_subject, const char* old_issuer,
476 const char* old_fingerprint, DWORD flags)
477{
478 JNIEnv* env;
479 jboolean attached = jni_attach_thread(&env);
480 jstring jhost = (*env)->NewStringUTF(env, host);
481 jstring jstr0 = (*env)->NewStringUTF(env, common_name);
482 jstring jstr1 = (*env)->NewStringUTF(env, subject);
483 jstring jstr2 = (*env)->NewStringUTF(env, issuer);
484 jstring jstr3 = (*env)->NewStringUTF(env, new_fingerprint);
485 jstring jstr4 = (*env)->NewStringUTF(env, old_subject);
486 jstring jstr5 = (*env)->NewStringUTF(env, old_issuer);
487 jstring jstr6 = (*env)->NewStringUTF(env, old_fingerprint);
488 jint res =
489 freerdp_callback_int_result("OnVerifyChangedCertificateEx",
490 "(JLjava/lang/String;JLjava/lang/String;Ljava/lang/"
491 "String;Ljava/lang/String;Ljava/lang/String;"
492 "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)I",
493 (jlong)instance, jhost, (jlong)port, jstr0, jstr1, jstr2, jstr3,
494 jstr4, jstr5, jstr6, (jlong)flags);
495
496 if (attached == JNI_TRUE)
497 jni_detach_thread();
498
499 return res;
500}
501
502static int android_freerdp_run(freerdp* instance)
503{
504 DWORD count;
505 DWORD status = WAIT_FAILED;
506 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
507 HANDLE inputEvent = nullptr;
508 const rdpSettings* settings = instance->context->settings;
509 rdpContext* context = instance->context;
510
511 inputEvent = android_get_handle(instance);
512
513 while (!freerdp_shall_disconnect_context(instance->context))
514 {
515 DWORD tmp;
516 count = 0;
517
518 handles[count++] = inputEvent;
519
520 tmp = freerdp_get_event_handles(context, &handles[count], 64 - count);
521
522 if (tmp == 0)
523 {
524 WLog_ERR(TAG, "freerdp_get_event_handles failed");
525 break;
526 }
527
528 count += tmp;
529 status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
530
531 if (status == WAIT_FAILED)
532 {
533 WLog_ERR(TAG, "WaitForMultipleObjects failed with %u [%08X]", status,
534 (unsigned)GetLastError());
535 break;
536 }
537
538 if (!freerdp_check_event_handles(context))
539 {
540 /* TODO: Auto reconnect
541 if (xf_auto_reconnect(instance))
542 continue;
543 */
544 WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
545 status = GetLastError();
546 break;
547 }
548
549 if (freerdp_shall_disconnect_context(instance->context))
550 break;
551
552 if (android_check_handle(instance) != TRUE)
553 {
554 WLog_ERR(TAG, "Failed to check android file descriptor");
555 status = GetLastError();
556 break;
557 }
558 }
559
560disconnect:
561 WLog_INFO(TAG, "Prepare shutdown...");
562
563 return status;
564}
565
566static DWORD WINAPI android_thread_func(LPVOID param)
567{
568 DWORD status = ERROR_BAD_ARGUMENTS;
569 freerdp* instance = param;
570 WLog_DBG(TAG, "Start...");
571
572 WINPR_ASSERT(instance);
573 WINPR_ASSERT(instance->context);
574
575 if (freerdp_client_start(instance->context) != CHANNEL_RC_OK)
576 goto fail;
577
578 WLog_DBG(TAG, "Connect...");
579
580 if (!freerdp_connect(instance))
581 status = GetLastError();
582 else
583 {
584 status = android_freerdp_run(instance);
585 WLog_DBG(TAG, "Disconnect...");
586
587 if (!freerdp_disconnect(instance))
588 status = GetLastError();
589 }
590
591 WLog_DBG(TAG, "Stop...");
592
593 if (freerdp_client_stop(instance->context) != CHANNEL_RC_OK)
594 goto fail;
595
596fail:
597 WLog_DBG(TAG, "Session ended with %08" PRIX32 "", status);
598
599 if (status == CHANNEL_RC_OK)
600 freerdp_callback("OnDisconnected", "(J)V", (jlong)instance);
601 else
602 freerdp_callback("OnConnectionFailure", "(J)V", (jlong)instance);
603
604 WLog_DBG(TAG, "Quit.");
605 ExitThread(status);
606 return status;
607}
608
609static BOOL android_client_new(freerdp* instance, rdpContext* context)
610{
611 WINPR_ASSERT(instance);
612 WINPR_ASSERT(context);
613
614 if (!android_event_queue_init(instance))
615 return FALSE;
616
617 instance->PreConnect = android_pre_connect;
618 instance->PostConnect = android_post_connect;
619 instance->PostDisconnect = android_post_disconnect;
620 instance->AuthenticateEx = android_authenticate_ex;
621 instance->VerifyCertificateEx = android_verify_certificate_ex;
622 instance->VerifyChangedCertificateEx = android_verify_changed_certificate_ex;
623 instance->LogonErrorInfo = nullptr;
624 return TRUE;
625}
626
627static void android_client_free(freerdp* instance, rdpContext* context)
628{
629 if (!context)
630 return;
631
632 android_event_queue_uninit(instance);
633}
634
635static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
636{
637 WINPR_ASSERT(pEntryPoints);
638
639 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
640
641 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
642 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
643 pEntryPoints->GlobalInit = nullptr;
644 pEntryPoints->GlobalUninit = nullptr;
645 pEntryPoints->ContextSize = sizeof(androidContext);
646 pEntryPoints->ClientNew = android_client_new;
647 pEntryPoints->ClientFree = android_client_free;
648 pEntryPoints->ClientStart = nullptr;
649 pEntryPoints->ClientStop = nullptr;
650 return 0;
651}
652
653JNIEXPORT jlong JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1new(
654 JNIEnv* env, jclass cls, jobject context)
655{
656 jclass contextClass;
657 jclass fileClass;
658 jobject filesDirObj;
659 jmethodID getFilesDirID;
660 jmethodID getAbsolutePathID;
661 jstring path;
662 const char* raw;
663 char* envStr;
664 RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
665 rdpContext* ctx;
666#if defined(WITH_GPROF)
667 setenv("CPUPROFILE_FREQUENCY", "200", 1);
668 monstartup("libfreerdp-android.so");
669#endif
670 contextClass = (*env)->FindClass(env, JAVA_CONTEXT_CLASS);
671 fileClass = (*env)->FindClass(env, JAVA_FILE_CLASS);
672
673 if (!contextClass || !fileClass)
674 {
675 WLog_FATAL(TAG, "Failed to load class references %s=%p, %s=%p", JAVA_CONTEXT_CLASS,
676 (void*)contextClass, JAVA_FILE_CLASS, (void*)fileClass);
677 return (jlong) nullptr;
678 }
679
680 getFilesDirID =
681 (*env)->GetMethodID(env, contextClass, "getFilesDir", "()L" JAVA_FILE_CLASS ";");
682
683 if (!getFilesDirID)
684 {
685 WLog_FATAL(TAG, "Failed to find method ID getFilesDir ()L" JAVA_FILE_CLASS ";");
686 return (jlong) nullptr;
687 }
688
689 getAbsolutePathID =
690 (*env)->GetMethodID(env, fileClass, "getAbsolutePath", "()Ljava/lang/String;");
691
692 if (!getAbsolutePathID)
693 {
694 WLog_FATAL(TAG, "Failed to find method ID getAbsolutePath ()Ljava/lang/String;");
695 return (jlong) nullptr;
696 }
697
698 filesDirObj = (*env)->CallObjectMethod(env, context, getFilesDirID);
699
700 if (!filesDirObj)
701 {
702 WLog_FATAL(TAG, "Failed to call getFilesDir");
703 return (jlong) nullptr;
704 }
705
706 path = (*env)->CallObjectMethod(env, filesDirObj, getAbsolutePathID);
707
708 if (!path)
709 {
710 WLog_FATAL(TAG, "Failed to call getAbsolutePath");
711 return (jlong) nullptr;
712 }
713
714 raw = (*env)->GetStringUTFChars(env, path, 0);
715
716 if (!raw)
717 {
718 WLog_FATAL(TAG, "Failed to get C string from java string");
719 return (jlong) nullptr;
720 }
721
722 envStr = _strdup(raw);
723 (*env)->ReleaseStringUTFChars(env, path, raw);
724
725 if (!envStr)
726 {
727 WLog_FATAL(TAG, "_strdup(%s) failed", raw);
728 return (jlong) nullptr;
729 }
730
731 if (setenv("HOME", _strdup(envStr), 1) != 0)
732 {
733 char ebuffer[256] = WINPR_C_ARRAY_INIT;
734 WLog_FATAL(TAG, "Failed to set environment HOME=%s %s [%d]", envStr,
735 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
736 return (jlong) nullptr;
737 }
738
739 RdpClientEntry(&clientEntryPoints);
740 ctx = freerdp_client_context_new(&clientEntryPoints);
741
742 if (!ctx)
743 return (jlong) nullptr;
744
745 return (jlong)ctx->instance;
746}
747
748JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1free(
749 JNIEnv* env, jclass cls, jlong instance)
750{
751 freerdp* inst = (freerdp*)instance;
752
753 if (inst)
754 freerdp_client_context_free(inst->context);
755
756#if defined(WITH_GPROF)
757 moncleanup();
758#endif
759}
760
761JNIEXPORT jstring JNICALL
762Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1last_1error_1string(JNIEnv* env,
763 jclass cls,
764 jlong instance)
765{
766 freerdp* inst = (freerdp*)instance;
767
768 if (!inst || !inst->context)
769 return (*env)->NewStringUTF(env, "");
770
771 return (*env)->NewStringUTF(
772 env, freerdp_get_last_error_string(freerdp_get_last_error(inst->context)));
773}
774
775JNIEXPORT jboolean JNICALL
776Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1parse_1arguments(JNIEnv* env, jclass cls,
777 jlong instance,
778 jobjectArray arguments)
779{
780 freerdp* inst = (freerdp*)instance;
781 int count;
782 char** argv;
783 DWORD status;
784
785 if (!inst || !inst->context)
786 return JNI_FALSE;
787
788 count = (*env)->GetArrayLength(env, arguments);
789 argv = calloc(count, sizeof(char*));
790
791 if (!argv)
792 return JNI_TRUE;
793
794 for (int i = 0; i < count; i++)
795 {
796 jstring str = (jstring)(*env)->GetObjectArrayElement(env, arguments, i);
797 const char* raw = (*env)->GetStringUTFChars(env, str, 0);
798 argv[i] = _strdup(raw);
799 (*env)->ReleaseStringUTFChars(env, str, raw);
800 }
801
802 status =
803 freerdp_client_settings_parse_command_line(inst->context->settings, count, argv, FALSE);
804
805 for (int i = 0; i < count; i++)
806 free(argv[i]);
807
808 free(argv);
809 return (status == 0) ? JNI_TRUE : JNI_FALSE;
810}
811
812JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1connect(
813 JNIEnv* env, jclass cls, jlong instance)
814{
815 freerdp* inst = (freerdp*)instance;
816
817 if (!inst || !inst->context)
818 {
819 WLog_FATAL(TAG, "(env=%p, cls=%p, instance=%" PRId64, (void*)env, (void*)cls,
820 (int64_t)instance);
821 return JNI_FALSE;
822 }
823
824 androidContext* ctx = (androidContext*)inst->context;
825
826 if (!(ctx->thread = CreateThread(nullptr, 0, android_thread_func, inst, 0, nullptr)))
827 {
828 return JNI_FALSE;
829 }
830
831 return JNI_TRUE;
832}
833
834JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1disconnect(
835 JNIEnv* env, jclass cls, jlong instance)
836{
837 freerdp* inst = (freerdp*)instance;
838
839 if (!inst || !inst->context || !cls || !env)
840 {
841 WLog_FATAL(TAG, "(env=%p, cls=%p, instance=%" PRId64, (void*)env, (void*)cls,
842 (int64_t)instance);
843 return JNI_FALSE;
844 }
845
846 androidContext* ctx = (androidContext*)inst->context;
847 ANDROID_EVENT* event = (ANDROID_EVENT*)android_event_disconnect_new();
848
849 if (!event)
850 return JNI_FALSE;
851
852 if (!android_push_event(inst, event))
853 {
854 android_event_free((ANDROID_EVENT*)event);
855 return JNI_FALSE;
856 }
857
858 if (!freerdp_abort_connect_context(inst->context))
859 return JNI_FALSE;
860
861 return JNI_TRUE;
862}
863
864JNIEXPORT jboolean JNICALL
865Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1update_1graphics(JNIEnv* env, jclass cls,
866 jlong instance,
867 jobject bitmap, jint x,
868 jint y, jint width,
869 jint height)
870{
871 UINT32 DstFormat;
872 jboolean rc;
873 int ret;
874 void* pixels;
875 AndroidBitmapInfo info;
876 freerdp* inst = (freerdp*)instance;
877 rdpGdi* gdi;
878
879 if (!env || !cls || !inst)
880 {
881 WLog_FATAL(TAG, "(env=%p, cls=%p, instance=%" PRId64, (void*)env, (void*)cls,
882 (int64_t)instance);
883 return JNI_FALSE;
884 }
885
886 gdi = inst->context->gdi;
887
888 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0)
889 {
890 WLog_FATAL(TAG, "AndroidBitmap_getInfo() failed ! error=%d", ret);
891 return JNI_FALSE;
892 }
893
894 if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0)
895 {
896 WLog_FATAL(TAG, "AndroidBitmap_lockPixels() failed ! error=%d", ret);
897 return JNI_FALSE;
898 }
899
900 rc = JNI_TRUE;
901
902 switch (info.format)
903 {
904 case ANDROID_BITMAP_FORMAT_RGBA_8888:
905 DstFormat = PIXEL_FORMAT_RGBX32;
906 break;
907
908 case ANDROID_BITMAP_FORMAT_RGB_565:
909 DstFormat = PIXEL_FORMAT_RGB16;
910 break;
911
912 case ANDROID_BITMAP_FORMAT_RGBA_4444:
913 case ANDROID_BITMAP_FORMAT_A_8:
914 case ANDROID_BITMAP_FORMAT_NONE:
915 default:
916 rc = JNI_FALSE;
917 break;
918 }
919
920 if (rc)
921 {
922 rc = freerdp_image_copy(pixels, DstFormat, info.stride, x, y, width, height,
923 gdi->primary_buffer, gdi->dstFormat, gdi->stride, x, y,
924 &gdi->palette, FREERDP_FLIP_NONE);
925 }
926
927 if ((ret = AndroidBitmap_unlockPixels(env, bitmap)) < 0)
928 {
929 WLog_FATAL(TAG, "AndroidBitmap_unlockPixels() failed ! error=%d", ret);
930 return JNI_FALSE;
931 }
932
933 return rc;
934}
935
936JNIEXPORT jboolean JNICALL
937Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1key_1event(JNIEnv* env, jclass cls,
938 jlong instance,
939 jint keycode,
940 jboolean down)
941{
942 DWORD scancode;
943 ANDROID_EVENT* event;
944 freerdp* inst = (freerdp*)instance;
945 scancode = GetVirtualScanCodeFromVirtualKeyCode(keycode, 4);
946 int flags = (down == JNI_TRUE) ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE;
947 flags |= (scancode & KBDEXT) ? KBD_FLAGS_EXTENDED : 0;
948 event = (ANDROID_EVENT*)android_event_key_new(flags, scancode & 0xFF);
949
950 if (!event)
951 return JNI_FALSE;
952
953 if (!android_push_event(inst, event))
954 {
955 android_event_free(event);
956 return JNI_FALSE;
957 }
958
959 WLog_DBG(TAG, "send_key_event: %" PRIu32 ", %d", scancode, flags);
960 return JNI_TRUE;
961}
962
963JNIEXPORT jboolean JNICALL
964Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1unicodekey_1event(
965 JNIEnv* env, jclass cls, jlong instance, jint keycode, jboolean down)
966{
967 ANDROID_EVENT* event;
968 freerdp* inst = (freerdp*)instance;
969 UINT16 flags = (down == JNI_TRUE) ? 0 : KBD_FLAGS_RELEASE;
970 event = (ANDROID_EVENT*)android_event_unicodekey_new(flags, keycode);
971
972 if (!event)
973 return JNI_FALSE;
974
975 if (!android_push_event(inst, event))
976 {
977 android_event_free(event);
978 return JNI_FALSE;
979 }
980
981 WLog_DBG(TAG, "send_unicodekey_event: %d", keycode);
982 return JNI_TRUE;
983}
984
985JNIEXPORT jboolean JNICALL
986Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1is_1unicode_1input_1supported(
987 JNIEnv* env, jclass cls, jlong instance)
988{
989 freerdp* inst = (freerdp*)instance;
990
991 if (!inst || !inst->context || !inst->context->settings)
992 return JNI_FALSE;
993
994 if (!freerdp_settings_get_bool(inst->context->settings, FreeRDP_UnicodeInput))
995 return JNI_FALSE;
996
997 return JNI_TRUE;
998}
999
1000JNIEXPORT jboolean JNICALL
1001Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1cursor_1event(
1002 JNIEnv* env, jclass cls, jlong instance, jint x, jint y, jint flags)
1003{
1004 ANDROID_EVENT* event;
1005 freerdp* inst = (freerdp*)instance;
1006 event = (ANDROID_EVENT*)android_event_cursor_new(flags, x, y);
1007
1008 if (!event)
1009 return JNI_FALSE;
1010
1011 if (!android_push_event(inst, event))
1012 {
1013 android_event_free(event);
1014 return JNI_FALSE;
1015 }
1016
1017 WLog_DBG(TAG, "send_cursor_event: (%d, %d), %d", x, y, flags);
1018 return JNI_TRUE;
1019}
1020
1021static jboolean android_push_clipboard_event(freerdp* inst, const void* data, size_t data_length,
1022 const char* mimeType)
1023{
1024 ANDROID_EVENT* event = (ANDROID_EVENT*)android_event_clipboard_new(data, data_length, mimeType);
1025 if (!event)
1026 return JNI_FALSE;
1027 if (!android_push_event(inst, event))
1028 {
1029 android_event_free(event);
1030 return JNI_FALSE;
1031 }
1032 return JNI_TRUE;
1033}
1034
1035JNIEXPORT jboolean JNICALL
1036Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1clipboard_1data(JNIEnv* env,
1037 jclass cls,
1038 jlong instance,
1039 jstring jdata)
1040{
1041 WINPR_UNUSED(cls);
1042 freerdp* inst = (freerdp*)instance;
1043 const char* data = jdata != nullptr ? (*env)->GetStringUTFChars(env, jdata, nullptr) : nullptr;
1044 const size_t data_length = data ? (*env)->GetStringUTFLength(env, jdata) : 0;
1045 jboolean ret = android_push_clipboard_event(inst, data, data_length, "text/plain");
1046 WLog_DBG(TAG, "send_clipboard_data: (%s)", data);
1047
1048 if (data)
1049 (*env)->ReleaseStringUTFChars(env, jdata, data);
1050
1051 return ret;
1052}
1053
1054static BOOL android_is_image_mime_supported(const char* mimeType)
1055{
1056 if (!mimeType)
1057 return FALSE;
1058 if (strcmp(mimeType, "image/png") == 0)
1059 return winpr_image_format_is_supported(WINPR_IMAGE_PNG);
1060 if (strcmp(mimeType, "image/jpeg") == 0 || strcmp(mimeType, "image/jpg") == 0)
1061 return winpr_image_format_is_supported(WINPR_IMAGE_JPEG);
1062 if (strcmp(mimeType, "image/webp") == 0)
1063 return winpr_image_format_is_supported(WINPR_IMAGE_WEBP);
1064 return FALSE;
1065}
1066
1067JNIEXPORT jboolean JNICALL
1068Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1clipboard_1image_1data(
1069 JNIEnv* env, jclass cls, jlong instance, jbyteArray jdata, jstring jmimeType)
1070{
1071 WINPR_UNUSED(cls);
1072 freerdp* inst = (freerdp*)instance;
1073 jsize data_length = (*env)->GetArrayLength(env, jdata);
1074 jbyte* data = (*env)->GetByteArrayElements(env, jdata, nullptr);
1075 const char* mimeType = jmimeType ? (*env)->GetStringUTFChars(env, jmimeType, nullptr) : nullptr;
1076 jboolean ret = JNI_FALSE;
1077 if (android_is_image_mime_supported(mimeType))
1078 ret = android_push_clipboard_event(inst, data, (size_t)data_length, mimeType);
1079
1080 if (mimeType)
1081 (*env)->ReleaseStringUTFChars(env, jmimeType, mimeType);
1082 (*env)->ReleaseByteArrayElements(env, jdata, data, JNI_ABORT);
1083 return ret;
1084}
1085
1086JNIEXPORT jboolean JNICALL
1087Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1monitor_1layout(
1088 JNIEnv* env, jclass cls, jlong instance, jint width, jint height)
1089{
1090 WINPR_UNUSED(env);
1091 WINPR_UNUSED(cls);
1092 freerdp* inst = (freerdp*)instance;
1093
1094 if (!inst || !inst->context)
1095 return JNI_FALSE;
1096
1097 androidContext* afc = (androidContext*)inst->context;
1098 return android_disp_send_monitor_layout(afc, (UINT32)width, (UINT32)height) ? JNI_TRUE
1099 : JNI_FALSE;
1100}
1101
1102JNIEXPORT jstring JNICALL
1103Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1jni_1version(JNIEnv* env, jclass cls)
1104{
1105 return (*env)->NewStringUTF(env, FREERDP_JNI_VERSION);
1106}
1107
1108JNIEXPORT jboolean JNICALL
1109Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1has_1h264(JNIEnv* env, jclass cls)
1110{
1111 H264_CONTEXT* ctx = h264_context_new(FALSE);
1112 if (!ctx)
1113 return JNI_FALSE;
1114 h264_context_free(ctx);
1115 return JNI_TRUE;
1116}
1117
1118JNIEXPORT jstring JNICALL
1119Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1version(JNIEnv* env, jclass cls)
1120{
1121 return (*env)->NewStringUTF(env, freerdp_get_version_string());
1122}
1123
1124JNIEXPORT jstring JNICALL
1125Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1build_1revision(JNIEnv* env,
1126 jclass cls)
1127{
1128 return (*env)->NewStringUTF(env, freerdp_get_build_revision());
1129}
1130
1131JNIEXPORT jstring JNICALL
1132Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1build_1config(JNIEnv* env,
1133 jclass cls)
1134{
1135 return (*env)->NewStringUTF(env, freerdp_get_build_config());
1136}
1137
1138jint JNI_OnLoad(JavaVM* vm, void* reserved)
1139{
1140 JNIEnv* env;
1141 setlocale(LC_ALL, "");
1142 WLog_DBG(TAG, "Setting up JNI environment...");
1143
1144 /*
1145 if (freerdp_handle_signals() != 0)
1146 {
1147 WLog_FATAL(TAG, "Failed to register signal handler");
1148 return -1;
1149 }
1150 */
1151 if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK)
1152 {
1153 WLog_FATAL(TAG, "Failed to get the environment");
1154 return -1;
1155 }
1156
1157 // Get SBCEngine activity class
1158 jclass activityClass = (*env)->FindClass(env, JAVA_LIBFREERDP_CLASS);
1159
1160 if (!activityClass)
1161 {
1162 WLog_FATAL(TAG, "failed to get %s class reference", JAVA_LIBFREERDP_CLASS);
1163 return -1;
1164 }
1165
1166 /* create global reference for class */
1167 gJavaActivityClass = (*env)->NewGlobalRef(env, activityClass);
1168 gOnPointerSetMethod =
1169 (*env)->GetStaticMethodID(env, gJavaActivityClass, "OnPointerSet", "(J[IIIII)V");
1170 g_JavaVm = vm;
1171 return init_callback_environment(vm, env);
1172}
1173
1174void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
1175{
1176 JNIEnv* env;
1177 WLog_DBG(TAG, "Tearing down JNI environment...");
1178
1179 if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK)
1180 {
1181 WLog_FATAL(TAG, "Failed to get the environment");
1182 return;
1183 }
1184
1185 if (gJavaActivityClass)
1186 (*env)->DeleteGlobalRef(env, gJavaActivityClass);
1187}
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.