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