FreeRDP
Loading...
Searching...
No Matches
LibFreeRDP.java
1/*
2 Android FreeRDP JNI Wrapper
3
4 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
5
6 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7 If a copy of the MPL was not distributed with this file, You can obtain one at
8 http://mozilla.org/MPL/2.0/.
9*/
10
11package com.freerdp.freerdpcore.services;
12
13import android.content.Context;
14import android.graphics.Bitmap;
15import android.net.Uri;
16import android.util.Log;
17
18import androidx.collection.LongSparseArray;
19
20import com.freerdp.freerdpcore.application.GlobalApp;
21import com.freerdp.freerdpcore.application.SessionState;
22import com.freerdp.freerdpcore.domain.BookmarkBase;
23import com.freerdp.freerdpcore.domain.ManualBookmark;
24import com.freerdp.freerdpcore.presentation.ApplicationSettingsActivity;
25
26import java.util.ArrayList;
27import java.util.Objects;
28import java.util.regex.Matcher;
29import java.util.regex.Pattern;
30
31public class LibFreeRDP
32{
33 private static final String TAG = "LibFreeRDP";
34 private static EventListener listener;
35 private static boolean mHasH264 = false;
36
37 private static final LongSparseArray<Boolean> mInstanceState = new LongSparseArray<>();
38
39 public static final long VERIFY_CERT_FLAG_NONE = 0x00;
40 public static final long VERIFY_CERT_FLAG_LEGACY = 0x02;
41 public static final long VERIFY_CERT_FLAG_REDIRECT = 0x10;
42 public static final long VERIFY_CERT_FLAG_GATEWAY = 0x20;
43 public static final long VERIFY_CERT_FLAG_CHANGED = 0x40;
44 public static final long VERIFY_CERT_FLAG_MISMATCH = 0x80;
45 public static final long VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1 = 0x100;
46 public static final long VERIFY_CERT_FLAG_FP_IS_PEM = 0x200;
47
48 private static boolean tryLoad(String[] libraries)
49 {
50 boolean success = false;
51 final String LD_PATH = System.getProperty("java.library.path");
52 for (String lib : libraries)
53 {
54 try
55 {
56 Log.v(TAG, "Trying to load library " + lib + " from LD_PATH: " + LD_PATH);
57 System.loadLibrary(lib);
58 success = true;
59 }
60 catch (UnsatisfiedLinkError e)
61 {
62 Log.e(TAG, "Failed to load library " + lib + ": " + e);
63 success = false;
64 break;
65 }
66 }
67
68 return success;
69 }
70
71 private static boolean tryLoad(String library)
72 {
73 return tryLoad(new String[] { library });
74 }
75
76 static
77 {
78 try
79 {
80 System.loadLibrary("freerdp-android");
81
82 /* Load dependent libraries too to trigger JNI_OnLoad calls */
83 String version = freerdp_get_jni_version();
84 String[] versions = version.split("[\\.-]");
85 if (versions.length > 0)
86 {
87 System.loadLibrary("freerdp-client" + versions[0]);
88 System.loadLibrary("freerdp" + versions[0]);
89 System.loadLibrary("winpr" + versions[0]);
90 }
91 Pattern pattern = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+).*");
92 Matcher matcher = pattern.matcher(version);
93 if (!matcher.matches() || (matcher.groupCount() < 3))
94 throw new RuntimeException("APK broken: native library version " + version +
95 " does not meet requirements!");
96 int major = Integer.parseInt(Objects.requireNonNull(matcher.group(1)));
97 int minor = Integer.parseInt(Objects.requireNonNull(matcher.group(2)));
98 int patch = Integer.parseInt(Objects.requireNonNull(matcher.group(3)));
99
100 if (major > 2)
101 mHasH264 = freerdp_has_h264();
102 else if (minor > 5)
103 mHasH264 = freerdp_has_h264();
104 else if ((minor == 5) && (patch >= 1))
105 mHasH264 = freerdp_has_h264();
106 else
107 throw new RuntimeException("APK broken: native library version " + version +
108 " does not meet requirements!");
109 Log.i(TAG, "Successfully loaded native library. H264 is " +
110 (mHasH264 ? "supported" : "not available"));
111 }
112 catch (UnsatisfiedLinkError e)
113 {
114 Log.e(TAG, "Failed to load library: " + e);
115 throw e;
116 }
117 }
118
119 public static boolean hasH264Support()
120 {
121 return mHasH264;
122 }
123
124 private static native boolean freerdp_has_h264();
125
126 private static native String freerdp_get_jni_version();
127
128 private static native String freerdp_get_version();
129
130 private static native String freerdp_get_build_revision();
131
132 private static native String freerdp_get_build_config();
133
134 private static native long freerdp_new(Context context);
135
136 private static native void freerdp_free(long inst);
137
138 private static native boolean freerdp_parse_arguments(long inst, String[] args);
139
140 private static native boolean freerdp_connect(long inst);
141
142 private static native boolean freerdp_disconnect(long inst);
143
144 private static native boolean freerdp_update_graphics(long inst, Bitmap bitmap, int x, int y,
145 int width, int height);
146
147 private static native boolean freerdp_send_cursor_event(long inst, int x, int y, int flags);
148
149 private static native boolean freerdp_send_key_event(long inst, int keycode, boolean down);
150
151 private static native boolean freerdp_send_unicodekey_event(long inst, int keycode,
152 boolean down);
153
154 private static native boolean freerdp_send_clipboard_data(long inst, String data);
155
156 private static native String freerdp_get_last_error_string(long inst);
157
158 public static void setEventListener(EventListener l)
159 {
160 listener = l;
161 }
162
163 public static long newInstance(Context context)
164 {
165 return freerdp_new(context);
166 }
167
168 public static void freeInstance(long inst)
169 {
170 synchronized (mInstanceState)
171 {
172 if (mInstanceState.get(inst, false))
173 {
174 freerdp_disconnect(inst);
175 }
176 while (mInstanceState.get(inst, false))
177 {
178 try
179 {
180 mInstanceState.wait();
181 }
182 catch (InterruptedException e)
183 {
184 throw new RuntimeException();
185 }
186 }
187 }
188 freerdp_free(inst);
189 }
190
191 public static boolean connect(long inst)
192 {
193 synchronized (mInstanceState)
194 {
195 if (mInstanceState.get(inst, false))
196 {
197 throw new RuntimeException("instance already connected");
198 }
199 }
200 return freerdp_connect(inst);
201 }
202
203 public static boolean disconnect(long inst)
204 {
205 synchronized (mInstanceState)
206 {
207 if (mInstanceState.get(inst, false))
208 {
209 return freerdp_disconnect(inst);
210 }
211 return true;
212 }
213 }
214
215 public static boolean cancelConnection(long inst)
216 {
217 synchronized (mInstanceState)
218 {
219 if (mInstanceState.get(inst, false))
220 {
221 return freerdp_disconnect(inst);
222 }
223 return true;
224 }
225 }
226
227 private static String addFlag(String name, boolean enabled)
228 {
229 if (enabled)
230 {
231 return "+" + name;
232 }
233 return "-" + name;
234 }
235
236 public static boolean setConnectionInfo(Context context, long inst, BookmarkBase bookmark)
237 {
238 BookmarkBase.ScreenSettings screenSettings = bookmark.getActiveScreenSettings();
239 BookmarkBase.AdvancedSettings advanced = bookmark.getAdvancedSettings();
240 BookmarkBase.DebugSettings debug = bookmark.getDebugSettings();
241
242 String arg;
243 ArrayList<String> args = new ArrayList<>();
244
245 args.add(TAG);
246 args.add("/gdi:sw");
247
248 final String clientName = ApplicationSettingsActivity.getClientName(context);
249 if (!clientName.isEmpty())
250 {
251 args.add("/client-hostname:" + clientName);
252 }
253 String certName = "";
254 if (bookmark.getType() != BookmarkBase.TYPE_MANUAL)
255 {
256 return false;
257 }
258
259 int port = bookmark.<ManualBookmark>get().getPort();
260 String hostname = bookmark.<ManualBookmark>get().getHostname();
261
262 args.add("/v:" + hostname);
263 args.add("/port:" + port);
264
265 arg = bookmark.getUsername();
266 if (!arg.isEmpty())
267 {
268 args.add("/u:" + arg);
269 }
270 arg = bookmark.getDomain();
271 if (!arg.isEmpty())
272 {
273 args.add("/d:" + arg);
274 }
275 arg = bookmark.getPassword();
276 if (!arg.isEmpty())
277 {
278 args.add("/p:" + arg);
279 }
280
281 args.add(
282 String.format("/size:%dx%d", screenSettings.getWidth(), screenSettings.getHeight()));
283 args.add("/bpp:" + screenSettings.getColors());
284
285 if (advanced.getConsoleMode())
286 {
287 args.add("/admin");
288 }
289
290 switch (advanced.getSecurity())
291 {
292 case 3: // NLA
293 args.add("/sec:nla");
294 break;
295 case 2: // TLS
296 args.add("/sec:tls");
297 break;
298 case 1: // RDP
299 args.add("/sec:rdp");
300 break;
301 default:
302 break;
303 }
304
305 if (!certName.isEmpty())
306 {
307 args.add("/cert-name:" + certName);
308 }
309
310 BookmarkBase.PerformanceFlags flags = bookmark.getActivePerformanceFlags();
311 if (flags.getRemoteFX())
312 {
313 args.add("/rfx");
314 args.add("/network:auto");
315 }
316
317 if (flags.getGfx())
318 {
319 args.add("/gfx");
320 args.add("/network:auto");
321 }
322
323 if (flags.getH264() && mHasH264)
324 {
325 args.add("/gfx:AVC444");
326 args.add("/network:auto");
327 }
328
329 args.add(addFlag("wallpaper", flags.getWallpaper()));
330 args.add(addFlag("window-drag", flags.getFullWindowDrag()));
331 args.add(addFlag("menu-anims", flags.getMenuAnimations()));
332 args.add(addFlag("themes", flags.getTheming()));
333 args.add(addFlag("fonts", flags.getFontSmoothing()));
334 args.add(addFlag("aero", flags.getDesktopComposition()));
335
336 if (!advanced.getRemoteProgram().isEmpty())
337 {
338 args.add("/shell:" + advanced.getRemoteProgram());
339 }
340
341 if (!advanced.getWorkDir().isEmpty())
342 {
343 args.add("/shell-dir:" + advanced.getWorkDir());
344 }
345
346 args.add(addFlag("async-channels", debug.getAsyncChannel()));
347 args.add(addFlag("async-update", debug.getAsyncUpdate()));
348
349 if (advanced.getRedirectSDCard())
350 {
351 String path = android.os.Environment.getExternalStorageDirectory().getPath();
352 args.add("/drive:sdcard," + path);
353 }
354
355 args.add("/clipboard");
356
357 // Gateway enabled?
358 if (bookmark.getType() == BookmarkBase.TYPE_MANUAL &&
359 bookmark.<ManualBookmark>get().getEnableGatewaySettings())
360 {
361 ManualBookmark.GatewaySettings gateway =
362 bookmark.<ManualBookmark>get().getGatewaySettings();
363
364 StringBuilder carg = new StringBuilder();
365 carg.append(
366 String.format("/gateway:g:%s:%d", gateway.getHostname(), gateway.getPort()));
367
368 arg = gateway.getUsername();
369 if (!arg.isEmpty())
370 {
371 carg.append(",u:" + arg);
372 }
373 arg = gateway.getDomain();
374 if (!arg.isEmpty())
375 {
376 carg.append(",d:" + arg);
377 }
378 arg = gateway.getPassword();
379 if (!arg.isEmpty())
380 {
381 carg.append(",p:" + arg);
382 }
383 args.add(carg.toString());
384 }
385
386 /* 0 ... local
387 1 ... remote
388 2 ... disable */
389 args.add("/audio-mode:" + advanced.getRedirectSound());
390 if (advanced.getRedirectSound() == 0)
391 {
392 args.add("/sound");
393 }
394
395 if (advanced.getRedirectMicrophone())
396 {
397 args.add("/microphone");
398 }
399
400 args.add("/kbd:unicode:on");
401 args.add("/cert:ignore");
402 args.add("/log-level:" + debug.getDebugLevel());
403 String[] arrayArgs = args.toArray(new String[0]);
404 return freerdp_parse_arguments(inst, arrayArgs);
405 }
406
407 public static boolean setConnectionInfo(Context context, long inst, Uri openUri)
408 {
409 ArrayList<String> args = new ArrayList<>();
410
411 // Parse URI from query string. Same key overwrite previous one
412 // freerdp://user@ip:port/connect?sound=&rfx=&p=password&clipboard=%2b&themes=-
413
414 // Now we only support Software GDI
415 args.add(TAG);
416 args.add("/gdi:sw");
417
418 final String clientName = ApplicationSettingsActivity.getClientName(context);
419 if (!clientName.isEmpty())
420 {
421 args.add("/client-hostname:" + clientName);
422 }
423
424 // Parse hostname and port. Set to 'v' argument
425 String hostname = openUri.getHost();
426 int port = openUri.getPort();
427 if (hostname != null)
428 {
429 hostname = hostname + ((port == -1) ? "" : (":" + port));
430 args.add("/v:" + hostname);
431 }
432
433 String user = openUri.getUserInfo();
434 if (user != null)
435 {
436 args.add("/u:" + user);
437 }
438
439 for (String key : openUri.getQueryParameterNames())
440 {
441 String value = openUri.getQueryParameter(key);
442
443 if (value.isEmpty())
444 {
445 // Query: key=
446 // To freerdp argument: /key
447 args.add("/" + key);
448 }
449 else if (value.equals("-") || value.equals("+"))
450 {
451 // Query: key=- or key=+
452 // To freerdp argument: -key or +key
453 args.add(value + key);
454 }
455 else
456 {
457 // Query: key=value
458 // To freerdp argument: /key:value
459 if (key.equals("drive") && value.equals("sdcard"))
460 {
461 // Special for sdcard redirect
462 String path = android.os.Environment.getExternalStorageDirectory().getPath();
463 value = "sdcard," + path;
464 }
465
466 args.add("/" + key + ":" + value);
467 }
468 }
469
470 String[] arrayArgs = args.toArray(new String[0]);
471 return freerdp_parse_arguments(inst, arrayArgs);
472 }
473
474 public static boolean updateGraphics(long inst, Bitmap bitmap, int x, int y, int width,
475 int height)
476 {
477 return freerdp_update_graphics(inst, bitmap, x, y, width, height);
478 }
479
480 public static boolean sendCursorEvent(long inst, int x, int y, int flags)
481 {
482 return freerdp_send_cursor_event(inst, x, y, flags);
483 }
484
485 public static boolean sendKeyEvent(long inst, int keycode, boolean down)
486 {
487 return freerdp_send_key_event(inst, keycode, down);
488 }
489
490 public static boolean sendUnicodeKeyEvent(long inst, int keycode, boolean down)
491 {
492 return freerdp_send_unicodekey_event(inst, keycode, down);
493 }
494
495 public static boolean sendClipboardData(long inst, String data)
496 {
497 return freerdp_send_clipboard_data(inst, data);
498 }
499
500 private static void OnConnectionSuccess(long inst)
501 {
502 if (listener != null)
503 listener.OnConnectionSuccess(inst);
504 synchronized (mInstanceState)
505 {
506 mInstanceState.append(inst, true);
507 mInstanceState.notifyAll();
508 }
509 }
510
511 private static void OnConnectionFailure(long inst)
512 {
513 if (listener != null)
514 listener.OnConnectionFailure(inst);
515 synchronized (mInstanceState)
516 {
517 mInstanceState.remove(inst);
518 mInstanceState.notifyAll();
519 }
520 }
521
522 private static void OnPreConnect(long inst)
523 {
524 if (listener != null)
525 listener.OnPreConnect(inst);
526 }
527
528 private static void OnDisconnecting(long inst)
529 {
530 if (listener != null)
531 listener.OnDisconnecting(inst);
532 }
533
534 private static void OnDisconnected(long inst)
535 {
536 if (listener != null)
537 listener.OnDisconnected(inst);
538 synchronized (mInstanceState)
539 {
540 mInstanceState.remove(inst);
541 mInstanceState.notifyAll();
542 }
543 }
544
545 private static void OnSettingsChanged(long inst, int width, int height, int bpp)
546 {
547 SessionState s = GlobalApp.getSession(inst);
548 if (s == null)
549 return;
550 UIEventListener uiEventListener = s.getUIEventListener();
551 if (uiEventListener != null)
552 uiEventListener.OnSettingsChanged(width, height, bpp);
553 }
554
555 private static boolean OnAuthenticate(long inst, StringBuilder username, StringBuilder domain,
556 StringBuilder password)
557 {
558 SessionState s = GlobalApp.getSession(inst);
559 if (s == null)
560 return false;
561 UIEventListener uiEventListener = s.getUIEventListener();
562 if (uiEventListener != null)
563 return uiEventListener.OnAuthenticate(username, domain, password);
564 return false;
565 }
566
567 private static boolean OnGatewayAuthenticate(long inst, StringBuilder username,
568 StringBuilder domain, StringBuilder password)
569 {
570 SessionState s = GlobalApp.getSession(inst);
571 if (s == null)
572 return false;
573 UIEventListener uiEventListener = s.getUIEventListener();
574 if (uiEventListener != null)
575 return uiEventListener.OnGatewayAuthenticate(username, domain, password);
576 return false;
577 }
578
579 private static int OnVerifyCertificateEx(long inst, String host, long port, String commonName,
580 String subject, String issuer, String fingerprint,
581 long flags)
582 {
583 SessionState s = GlobalApp.getSession(inst);
584 if (s == null)
585 return 0;
586 UIEventListener uiEventListener = s.getUIEventListener();
587 if (uiEventListener != null)
588 return uiEventListener.OnVerifiyCertificateEx(host, port, commonName, subject, issuer,
589 fingerprint, flags);
590 return 0;
591 }
592
593 private static int OnVerifyChangedCertificateEx(long inst, String host, long port,
594 String commonName, String subject,
595 String issuer, String fingerprint,
596 String oldSubject, String oldIssuer,
597 String oldFingerprint, long flags)
598 {
599 SessionState s = GlobalApp.getSession(inst);
600 if (s == null)
601 return 0;
602 UIEventListener uiEventListener = s.getUIEventListener();
603 if (uiEventListener != null)
604 return uiEventListener.OnVerifyChangedCertificateEx(host, port, commonName, subject,
605 issuer, fingerprint, oldSubject,
606 oldIssuer, oldFingerprint, flags);
607 return 0;
608 }
609
610 private static void OnGraphicsUpdate(long inst, int x, int y, int width, int height)
611 {
612 SessionState s = GlobalApp.getSession(inst);
613 if (s == null)
614 return;
615 UIEventListener uiEventListener = s.getUIEventListener();
616 if (uiEventListener != null)
617 uiEventListener.OnGraphicsUpdate(x, y, width, height);
618 }
619
620 private static void OnGraphicsResize(long inst, int width, int height, int bpp)
621 {
622 SessionState s = GlobalApp.getSession(inst);
623 if (s == null)
624 return;
625 UIEventListener uiEventListener = s.getUIEventListener();
626 if (uiEventListener != null)
627 uiEventListener.OnGraphicsResize(width, height, bpp);
628 }
629
630 private static void OnRemoteClipboardChanged(long inst, String data)
631 {
632 SessionState s = GlobalApp.getSession(inst);
633 if (s == null)
634 return;
635 UIEventListener uiEventListener = s.getUIEventListener();
636 if (uiEventListener != null)
637 uiEventListener.OnRemoteClipboardChanged(data);
638 }
639
640 public static String getVersion()
641 {
642 return freerdp_get_version();
643 }
644
645 public interface EventListener
646 {
647 void OnPreConnect(long instance);
648
649 void OnConnectionSuccess(long instance);
650
651 void OnConnectionFailure(long instance);
652
653 void OnDisconnecting(long instance);
654
655 void OnDisconnected(long instance);
656 }
657
658 public interface UIEventListener
659 {
660 void OnSettingsChanged(int width, int height, int bpp);
661
662 boolean OnAuthenticate(StringBuilder username, StringBuilder domain,
663 StringBuilder password);
664
665 boolean OnGatewayAuthenticate(StringBuilder username, StringBuilder domain,
666 StringBuilder password);
667
668 int OnVerifiyCertificateEx(String host, long port, String commonName, String subject, String issuer,
669 String fingerprint, long flags);
670
671 int OnVerifyChangedCertificateEx(String host, long port, String commonName, String subject, String issuer,
672 String fingerprint, String oldSubject, String oldIssuer,
673 String oldFingerprint, long flags);
674
675 void OnGraphicsUpdate(int x, int y, int width, int height);
676
677 void OnGraphicsResize(int width, int height, int bpp);
678
679 void OnRemoteClipboardChanged(String data);
680 }
681}