FreeRDP
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
60 static 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 
83 static 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 
106 static BOOL android_begin_paint(rdpContext* context)
107 {
108  return TRUE;
109 }
110 
111 static 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 
170 static 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 
183 static 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 
218 static 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 
227 static void android_Pointer_Free(rdpContext* context, rdpPointer* pointer)
228 {
229  WINPR_ASSERT(context);
230 }
231 
232 static BOOL android_Pointer_Set(rdpContext* context, rdpPointer* pointer)
233 {
234  WINPR_ASSERT(context);
235  WINPR_ASSERT(pointer);
236 
237  return TRUE;
238 }
239 
240 static BOOL android_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
241 {
242  WINPR_ASSERT(context);
243 
244  return TRUE;
245 }
246 
247 static BOOL android_Pointer_SetNull(rdpContext* context)
248 {
249  WINPR_ASSERT(context);
250 
251  return TRUE;
252 }
253 
254 static BOOL android_Pointer_SetDefault(rdpContext* context)
255 {
256  WINPR_ASSERT(context);
257 
258  return TRUE;
259 }
260 
261 static 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 
279 static 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 
310 static void android_post_disconnect(freerdp* instance)
311 {
312  freerdp_callback("OnDisconnecting", "(J)V", (jlong)instance);
313  gdi_free(instance);
314 }
315 
316 static 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 
348 static BOOL android_authenticate(freerdp* instance, char** username, char** password, char** domain)
349 {
350  return android_authenticate_int(instance, username, password, domain, "OnAuthenticate");
351 }
352 
353 static 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 
359 static 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 
390 static 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 
420 static 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 
478 disconnect:
479  WLog_INFO(TAG, "Prepare shutdown...");
480 
481  return status;
482 }
483 
484 static 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, "Disonnect...");
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 
514 fail:
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 
527 static 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 
546 static void android_client_free(freerdp* instance, rdpContext* context)
547 {
548  if (!context)
549  return;
550 
551  android_event_queue_uninit(instance);
552 }
553 
554 static 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 
572 JNIEXPORT 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 environemnt 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 
667 JNIEXPORT 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 
680 JNIEXPORT jstring JNICALL
681 Java_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 
694 JNIEXPORT jboolean JNICALL
695 Java_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 
731 JNIEXPORT 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 
753 JNIEXPORT 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 
784 JNIEXPORT jboolean JNICALL
785 Java_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 
855 JNIEXPORT jboolean JNICALL
856 Java_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 
882 JNIEXPORT jboolean JNICALL
883 Java_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 
904 JNIEXPORT jboolean JNICALL
905 Java_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 
925 JNIEXPORT jboolean JNICALL
926 Java_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;
949 out_fail:
950 
951  if (data)
952  (*env)->ReleaseStringUTFChars(env, jdata, data);
953 
954  return ret;
955 }
956 
957 JNIEXPORT jstring JNICALL
958 Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1jni_1version(JNIEnv* env, jclass cls)
959 {
960  return (*env)->NewStringUTF(env, FREERDP_JNI_VERSION);
961 }
962 
963 JNIEXPORT jboolean JNICALL
964 Java_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 
973 JNIEXPORT jstring JNICALL
974 Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1version(JNIEnv* env, jclass cls)
975 {
976  return (*env)->NewStringUTF(env, freerdp_get_version_string());
977 }
978 
979 JNIEXPORT jstring JNICALL
980 Java_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 
986 JNIEXPORT jstring JNICALL
987 Java_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 
993 static jclass gJavaActivityClass = NULL;
994 
995 jint JNI_OnLoad(JavaVM* vm, void* reserved)
996 {
997  JNIEnv* env;
998  setlocale(LC_ALL, "");
999  WLog_DBG(TAG, "Setting up JNI environement...");
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 
1029 void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
1030 {
1031  JNIEnv* env;
1032  WLog_DBG(TAG, "Tearing down JNI environement...");
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.