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