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.presentation.ApplicationSettingsActivity;
24
25import java.util.ArrayList;
26import java.util.List;
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 return freerdp_disconnect(inst);
218 }
219
220 private static String addFlag(String name, boolean enabled)
221 {
222 if (enabled)
223 {
224 return "+" + name;
225 }
226 return "-" + name;
227 }
228
229 public static boolean setConnectionInfo(Context context, long inst, BookmarkBase bookmark)
230 {
231 BookmarkBase.ScreenSettings screenSettings = bookmark.getActiveScreenSettings();
232 BookmarkBase.AdvancedSettings advanced = bookmark.getAdvancedSettings();
233 BookmarkBase.DebugSettings debug = bookmark.getDebugSettings();
234
235 String arg;
236 ArrayList<String> args = new ArrayList<>();
237
238 args.add(TAG);
239 args.add("/gdi:sw");
240
241 final String clientName = ApplicationSettingsActivity.getClientName(context);
242 if (!clientName.isEmpty())
243 {
244 args.add("/client-hostname:" + clientName);
245 }
246 String certName = "";
247 if (bookmark.getType() != BookmarkBase.TYPE_MANUAL)
248 {
249 return false;
250 }
251
252 int port = bookmark.getPort();
253 String hostname = bookmark.getHostname();
254
255 args.add("/v:" + hostname);
256 args.add("/port:" + port);
257
258 final int level = advanced.getTlsSecLevel();
259 List<String> tls = new ArrayList<>();
260
261 if (level >= 0)
262 {
263 tls.add("seclevel:" + level);
264 }
265
266 final int tlsMinLevel = advanced.getTlsMinLevel();
267 if (tlsMinLevel >= 0)
268 {
269 tls.add("enforce:" + tlsMinLevel);
270 }
271
272 if (!tls.isEmpty())
273 {
274 StringBuilder sb = new StringBuilder();
275 for (String s : tls)
276 {
277 if (sb.length() > 0)
278 {
279 sb.append(',');
280 }
281 sb.append(s);
282 }
283 args.add("/tls:" + sb);
284 }
285
286 arg = bookmark.getUsername();
287 if (!arg.isEmpty())
288 {
289 args.add("/u:" + arg);
290 }
291 arg = bookmark.getDomain();
292 if (!arg.isEmpty())
293 {
294 args.add("/d:" + arg);
295 }
296 arg = bookmark.getPassword();
297 if (!arg.isEmpty())
298 {
299 args.add("/p:" + arg);
300 }
301
302 args.add(
303 String.format("/size:%dx%d", screenSettings.getWidth(), screenSettings.getHeight()));
304 args.add("/bpp:" + screenSettings.getColors());
305
306 if (advanced.getConsoleMode())
307 {
308 args.add("/admin");
309 }
310
311 switch (advanced.getSecurity())
312 {
313 case 3: // NLA
314 args.add("/sec:nla");
315 break;
316 case 2: // TLS
317 args.add("/sec:tls");
318 break;
319 case 1: // RDP
320 args.add("/sec:rdp");
321 break;
322 default:
323 break;
324 }
325
326 if (!certName.isEmpty())
327 {
328 args.add("/cert-name:" + certName);
329 }
330
331 BookmarkBase.PerformanceFlags flags = bookmark.getActivePerformanceFlags();
332 if (flags.getRemoteFX())
333 {
334 args.add("/rfx");
335 args.add("/network:auto");
336 }
337
338 if (flags.getGfx())
339 {
340 args.add("/gfx");
341 args.add("/network:auto");
342 }
343
344 if (flags.getH264() && mHasH264)
345 {
346 args.add("/gfx:AVC444");
347 args.add("/network:auto");
348 }
349
350 args.add(addFlag("wallpaper", flags.getWallpaper()));
351 args.add(addFlag("window-drag", flags.getFullWindowDrag()));
352 args.add(addFlag("menu-anims", flags.getMenuAnimations()));
353 args.add(addFlag("themes", flags.getTheming()));
354 args.add(addFlag("fonts", flags.getFontSmoothing()));
355 args.add(addFlag("aero", flags.getDesktopComposition()));
356
357 if (!advanced.getRemoteProgram().isEmpty())
358 {
359 args.add("/shell:" + advanced.getRemoteProgram());
360 }
361
362 if (!advanced.getWorkDir().isEmpty())
363 {
364 args.add("/shell-dir:" + advanced.getWorkDir());
365 }
366
367 args.add(addFlag("async-channels", debug.getAsyncChannel()));
368 args.add(addFlag("async-update", debug.getAsyncUpdate()));
369
370 if (advanced.getRedirectSDCard())
371 {
372 String path = android.os.Environment.getExternalStorageDirectory().getPath();
373 args.add("/drive:sdcard," + path);
374 }
375
376 args.add("/clipboard");
377
378 // Gateway enabled?
379 if (bookmark.getType() == BookmarkBase.TYPE_MANUAL && bookmark.getEnableGatewaySettings())
380 {
381 BookmarkBase.GatewaySettings gateway = bookmark.getGatewaySettings();
382
383 StringBuilder carg = new StringBuilder();
384 carg.append(
385 String.format("/gateway:g:%s:%d", gateway.getHostname(), gateway.getPort()));
386
387 arg = gateway.getUsername();
388 if (!arg.isEmpty())
389 {
390 carg.append(",u:" + arg);
391 }
392 arg = gateway.getDomain();
393 if (!arg.isEmpty())
394 {
395 carg.append(",d:" + arg);
396 }
397 arg = gateway.getPassword();
398 if (!arg.isEmpty())
399 {
400 carg.append(",p:" + arg);
401 }
402 args.add(carg.toString());
403 }
404
405 /* 0 ... local
406 1 ... remote
407 2 ... disable */
408 args.add("/audio-mode:" + advanced.getRedirectSound());
409 if (advanced.getRedirectSound() == 0)
410 {
411 args.add("/sound");
412 }
413
414 if (advanced.getRedirectMicrophone())
415 {
416 args.add("/microphone");
417 }
418
419 args.add("/kbd:unicode:on");
420 args.add("/cert:ignore");
421 args.add("/log-level:" + debug.getDebugLevel());
422 String[] arrayArgs = args.toArray(new String[0]);
423 return freerdp_parse_arguments(inst, arrayArgs);
424 }
425
426 public static boolean setConnectionInfo(Context context, long inst, Uri openUri)
427 {
428 ArrayList<String> args = new ArrayList<>();
429
430 // Parse URI from query string. Same key overwrite previous one
431 // freerdp://user@ip:port/connect?sound=&rfx=&p=password&clipboard=%2b&themes=-
432
433 // Now we only support Software GDI
434 args.add(TAG);
435 args.add("/gdi:sw");
436
437 final String clientName = ApplicationSettingsActivity.getClientName(context);
438 if (!clientName.isEmpty())
439 {
440 args.add("/client-hostname:" + clientName);
441 }
442
443 // Parse hostname and port. Set to 'v' argument
444 String hostname = openUri.getHost();
445 int port = openUri.getPort();
446 if (hostname != null)
447 {
448 hostname = hostname + ((port == -1) ? "" : (":" + port));
449 args.add("/v:" + hostname);
450 }
451
452 String user = openUri.getUserInfo();
453 if (user != null)
454 {
455 args.add("/u:" + user);
456 }
457
458 for (String key : openUri.getQueryParameterNames())
459 {
460 String value = openUri.getQueryParameter(key);
461
462 if (value.isEmpty())
463 {
464 // Query: key=
465 // To freerdp argument: /key
466 args.add("/" + key);
467 }
468 else if (value.equals("-") || value.equals("+"))
469 {
470 // Query: key=- or key=+
471 // To freerdp argument: -key or +key
472 args.add(value + key);
473 }
474 else
475 {
476 // Query: key=value
477 // To freerdp argument: /key:value
478 if (key.equals("drive") && value.equals("sdcard"))
479 {
480 // Special for sdcard redirect
481 String path = android.os.Environment.getExternalStorageDirectory().getPath();
482 value = "sdcard," + path;
483 }
484
485 args.add("/" + key + ":" + value);
486 }
487 }
488
489 String[] arrayArgs = args.toArray(new String[0]);
490 return freerdp_parse_arguments(inst, arrayArgs);
491 }
492
493 public static boolean updateGraphics(long inst, Bitmap bitmap, int x, int y, int width,
494 int height)
495 {
496 return freerdp_update_graphics(inst, bitmap, x, y, width, height);
497 }
498
499 public static boolean sendCursorEvent(long inst, int x, int y, int flags)
500 {
501 return freerdp_send_cursor_event(inst, x, y, flags);
502 }
503
504 public static boolean sendKeyEvent(long inst, int keycode, boolean down)
505 {
506 return freerdp_send_key_event(inst, keycode, down);
507 }
508
509 public static boolean sendUnicodeKeyEvent(long inst, int keycode, boolean down)
510 {
511 return freerdp_send_unicodekey_event(inst, keycode, down);
512 }
513
514 public static boolean sendClipboardData(long inst, String data)
515 {
516 return freerdp_send_clipboard_data(inst, data);
517 }
518
519 private static void OnConnectionSuccess(long inst)
520 {
521 if (listener != null)
522 listener.OnConnectionSuccess(inst);
523 synchronized (mInstanceState)
524 {
525 mInstanceState.append(inst, true);
526 mInstanceState.notifyAll();
527 }
528 }
529
530 private static void OnConnectionFailure(long inst)
531 {
532 if (listener != null)
533 listener.OnConnectionFailure(inst);
534 synchronized (mInstanceState)
535 {
536 mInstanceState.remove(inst);
537 mInstanceState.notifyAll();
538 }
539 }
540
541 private static void OnPreConnect(long inst)
542 {
543 if (listener != null)
544 listener.OnPreConnect(inst);
545 }
546
547 private static void OnDisconnecting(long inst)
548 {
549 if (listener != null)
550 listener.OnDisconnecting(inst);
551 }
552
553 private static void OnDisconnected(long inst)
554 {
555 if (listener != null)
556 listener.OnDisconnected(inst);
557 synchronized (mInstanceState)
558 {
559 mInstanceState.remove(inst);
560 mInstanceState.notifyAll();
561 }
562 }
563
564 private static void OnSettingsChanged(long inst, int width, int height, int bpp)
565 {
566 SessionState s = GlobalApp.getSession(inst);
567 if (s == null)
568 return;
569 UIEventListener uiEventListener = s.getUIEventListener();
570 if (uiEventListener != null)
571 uiEventListener.OnSettingsChanged(width, height, bpp);
572 }
573
574 private static boolean OnAuthenticate(long inst, StringBuilder username, StringBuilder domain,
575 StringBuilder password)
576 {
577 SessionState s = GlobalApp.getSession(inst);
578 if (s == null)
579 return false;
580 UIEventListener uiEventListener = s.getUIEventListener();
581 if (uiEventListener != null)
582 return uiEventListener.OnAuthenticate(username, domain, password);
583 return false;
584 }
585
586 private static boolean OnGatewayAuthenticate(long inst, StringBuilder username,
587 StringBuilder domain, StringBuilder password)
588 {
589 SessionState s = GlobalApp.getSession(inst);
590 if (s == null)
591 return false;
592 UIEventListener uiEventListener = s.getUIEventListener();
593 if (uiEventListener != null)
594 return uiEventListener.OnGatewayAuthenticate(username, domain, password);
595 return false;
596 }
597
598 private static int OnVerifyCertificateEx(long inst, String host, long port, String commonName,
599 String subject, String issuer, String fingerprint,
600 long flags)
601 {
602 SessionState s = GlobalApp.getSession(inst);
603 if (s == null)
604 return 0;
605 UIEventListener uiEventListener = s.getUIEventListener();
606 if (uiEventListener != null)
607 return uiEventListener.OnVerifiyCertificateEx(host, port, commonName, subject, issuer,
608 fingerprint, flags);
609 return 0;
610 }
611
612 private static int OnVerifyChangedCertificateEx(long inst, String host, long port,
613 String commonName, String subject,
614 String issuer, String fingerprint,
615 String oldSubject, String oldIssuer,
616 String oldFingerprint, long flags)
617 {
618 SessionState s = GlobalApp.getSession(inst);
619 if (s == null)
620 return 0;
621 UIEventListener uiEventListener = s.getUIEventListener();
622 if (uiEventListener != null)
623 return uiEventListener.OnVerifyChangedCertificateEx(host, port, commonName, subject,
624 issuer, fingerprint, oldSubject,
625 oldIssuer, oldFingerprint, flags);
626 return 0;
627 }
628
629 private static void OnGraphicsUpdate(long inst, int x, int y, int width, int height)
630 {
631 SessionState s = GlobalApp.getSession(inst);
632 if (s == null)
633 return;
634 UIEventListener uiEventListener = s.getUIEventListener();
635 if (uiEventListener != null)
636 uiEventListener.OnGraphicsUpdate(x, y, width, height);
637 }
638
639 private static void OnGraphicsResize(long inst, int width, int height, int bpp)
640 {
641 SessionState s = GlobalApp.getSession(inst);
642 if (s == null)
643 return;
644 UIEventListener uiEventListener = s.getUIEventListener();
645 if (uiEventListener != null)
646 uiEventListener.OnGraphicsResize(width, height, bpp);
647 }
648
649 private static void OnRemoteClipboardChanged(long inst, String data)
650 {
651 SessionState s = GlobalApp.getSession(inst);
652 if (s == null)
653 return;
654 UIEventListener uiEventListener = s.getUIEventListener();
655 if (uiEventListener != null)
656 uiEventListener.OnRemoteClipboardChanged(data);
657 }
658
659 public static String getVersion()
660 {
661 return freerdp_get_version();
662 }
663
664 public interface EventListener
665 {
666 void OnPreConnect(long instance);
667
668 void OnConnectionSuccess(long instance);
669
670 void OnConnectionFailure(long instance);
671
672 void OnDisconnecting(long instance);
673
674 void OnDisconnected(long instance);
675 }
676
677 public interface UIEventListener
678 {
679 void OnSettingsChanged(int width, int height, int bpp);
680
681 boolean OnAuthenticate(StringBuilder username, StringBuilder domain,
682 StringBuilder password);
683
684 boolean OnGatewayAuthenticate(StringBuilder username, StringBuilder domain,
685 StringBuilder password);
686
687 int OnVerifiyCertificateEx(String host, long port, String commonName, String subject, String issuer,
688 String fingerprint, long flags);
689
690 int OnVerifyChangedCertificateEx(String host, long port, String commonName, String subject, String issuer,
691 String fingerprint, String oldSubject, String oldIssuer,
692 String oldFingerprint, long flags);
693
694 void OnGraphicsUpdate(int x, int y, int width, int height);
695
696 void OnGraphicsResize(int width, int height, int bpp);
697
698 void OnRemoteClipboardChanged(String data);
699 }
700}