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 String carg =
365 String.format("/gateway:g:%s:%d", gateway.getHostname(), gateway.getPort());
366
367 arg = gateway.getUsername();
368 if (!arg.isEmpty())
369 {
370 carg.append(",u:" + arg);
371 }
372 arg = gateway.getDomain();
373 if (!arg.isEmpty())
374 {
375 carg.append(",d:" + arg);
376 }
377 arg = gateway.getPassword();
378 if (!arg.isEmpty())
379 {
380 cargs.append(",p:" + arg);
381 }
382 args.add(String.format("/gateway:g:%s:%d", gateway.getHostname(), gateway.getPort()));
383 }
384
385 /* 0 ... local
386 1 ... remote
387 2 ... disable */
388 args.add("/audio-mode:" + advanced.getRedirectSound());
389 if (advanced.getRedirectSound() == 0)
390 {
391 args.add("/sound");
392 }
393
394 if (advanced.getRedirectMicrophone())
395 {
396 args.add("/microphone");
397 }
398
399 args.add("/kbd:unicode:on");
400 args.add("/cert:ignore");
401 args.add("/log-level:" + debug.getDebugLevel());
402 String[] arrayArgs = args.toArray(new String[0]);
403 return freerdp_parse_arguments(inst, arrayArgs);
404 }
405
406 public static boolean setConnectionInfo(Context context, long inst, Uri openUri)
407 {
408 ArrayList<String> args = new ArrayList<>();
409
410 // Parse URI from query string. Same key overwrite previous one
411 // freerdp://user@ip:port/connect?sound=&rfx=&p=password&clipboard=%2b&themes=-
412
413 // Now we only support Software GDI
414 args.add(TAG);
415 args.add("/gdi:sw");
416
417 final String clientName = ApplicationSettingsActivity.getClientName(context);
418 if (!clientName.isEmpty())
419 {
420 args.add("/client-hostname:" + clientName);
421 }
422
423 // Parse hostname and port. Set to 'v' argument
424 String hostname = openUri.getHost();
425 int port = openUri.getPort();
426 if (hostname != null)
427 {
428 hostname = hostname + ((port == -1) ? "" : (":" + port));
429 args.add("/v:" + hostname);
430 }
431
432 String user = openUri.getUserInfo();
433 if (user != null)
434 {
435 args.add("/u:" + user);
436 }
437
438 for (String key : openUri.getQueryParameterNames())
439 {
440 String value = openUri.getQueryParameter(key);
441
442 if (value.isEmpty())
443 {
444 // Query: key=
445 // To freerdp argument: /key
446 args.add("/" + key);
447 }
448 else if (value.equals("-") || value.equals("+"))
449 {
450 // Query: key=- or key=+
451 // To freerdp argument: -key or +key
452 args.add(value + key);
453 }
454 else
455 {
456 // Query: key=value
457 // To freerdp argument: /key:value
458 if (key.equals("drive") && value.equals("sdcard"))
459 {
460 // Special for sdcard redirect
461 String path = android.os.Environment.getExternalStorageDirectory().getPath();
462 value = "sdcard," + path;
463 }
464
465 args.add("/" + key + ":" + value);
466 }
467 }
468
469 String[] arrayArgs = args.toArray(new String[0]);
470 return freerdp_parse_arguments(inst, arrayArgs);
471 }
472
473 public static boolean updateGraphics(long inst, Bitmap bitmap, int x, int y, int width,
474 int height)
475 {
476 return freerdp_update_graphics(inst, bitmap, x, y, width, height);
477 }
478
479 public static boolean sendCursorEvent(long inst, int x, int y, int flags)
480 {
481 return freerdp_send_cursor_event(inst, x, y, flags);
482 }
483
484 public static boolean sendKeyEvent(long inst, int keycode, boolean down)
485 {
486 return freerdp_send_key_event(inst, keycode, down);
487 }
488
489 public static boolean sendUnicodeKeyEvent(long inst, int keycode, boolean down)
490 {
491 return freerdp_send_unicodekey_event(inst, keycode, down);
492 }
493
494 public static boolean sendClipboardData(long inst, String data)
495 {
496 return freerdp_send_clipboard_data(inst, data);
497 }
498
499 private static void OnConnectionSuccess(long inst)
500 {
501 if (listener != null)
502 listener.OnConnectionSuccess(inst);
503 synchronized (mInstanceState)
504 {
505 mInstanceState.append(inst, true);
506 mInstanceState.notifyAll();
507 }
508 }
509
510 private static void OnConnectionFailure(long inst)
511 {
512 if (listener != null)
513 listener.OnConnectionFailure(inst);
514 synchronized (mInstanceState)
515 {
516 mInstanceState.remove(inst);
517 mInstanceState.notifyAll();
518 }
519 }
520
521 private static void OnPreConnect(long inst)
522 {
523 if (listener != null)
524 listener.OnPreConnect(inst);
525 }
526
527 private static void OnDisconnecting(long inst)
528 {
529 if (listener != null)
530 listener.OnDisconnecting(inst);
531 }
532
533 private static void OnDisconnected(long inst)
534 {
535 if (listener != null)
536 listener.OnDisconnected(inst);
537 synchronized (mInstanceState)
538 {
539 mInstanceState.remove(inst);
540 mInstanceState.notifyAll();
541 }
542 }
543
544 private static void OnSettingsChanged(long inst, int width, int height, int bpp)
545 {
546 SessionState s = GlobalApp.getSession(inst);
547 if (s == null)
548 return;
549 UIEventListener uiEventListener = s.getUIEventListener();
550 if (uiEventListener != null)
551 uiEventListener.OnSettingsChanged(width, height, bpp);
552 }
553
554 private static boolean OnAuthenticate(long inst, StringBuilder username, StringBuilder domain,
555 StringBuilder password)
556 {
557 SessionState s = GlobalApp.getSession(inst);
558 if (s == null)
559 return false;
560 UIEventListener uiEventListener = s.getUIEventListener();
561 if (uiEventListener != null)
562 return uiEventListener.OnAuthenticate(username, domain, password);
563 return false;
564 }
565
566 private static boolean OnGatewayAuthenticate(long inst, StringBuilder username,
567 StringBuilder domain, StringBuilder password)
568 {
569 SessionState s = GlobalApp.getSession(inst);
570 if (s == null)
571 return false;
572 UIEventListener uiEventListener = s.getUIEventListener();
573 if (uiEventListener != null)
574 return uiEventListener.OnGatewayAuthenticate(username, domain, password);
575 return false;
576 }
577
578 private static int OnVerifyCertificateEx(long inst, String host, long port, String commonName,
579 String subject, String issuer, String fingerprint,
580 long flags)
581 {
582 SessionState s = GlobalApp.getSession(inst);
583 if (s == null)
584 return 0;
585 UIEventListener uiEventListener = s.getUIEventListener();
586 if (uiEventListener != null)
587 return uiEventListener.OnVerifiyCertificateEx(host, port, commonName, subject, issuer,
588 fingerprint, flags);
589 return 0;
590 }
591
592 private static int OnVerifyChangedCertificateEx(long inst, String host, long port,
593 String commonName, String subject,
594 String issuer, String fingerprint,
595 String oldSubject, String oldIssuer,
596 String oldFingerprint, long flags)
597 {
598 SessionState s = GlobalApp.getSession(inst);
599 if (s == null)
600 return 0;
601 UIEventListener uiEventListener = s.getUIEventListener();
602 if (uiEventListener != null)
603 return uiEventListener.OnVerifyChangedCertificateEx(host, port, commonName, subject,
604 issuer, fingerprint, oldSubject,
605 oldIssuer, oldFingerprint, flags);
606 return 0;
607 }
608
609 private static void OnGraphicsUpdate(long inst, int x, int y, int width, int height)
610 {
611 SessionState s = GlobalApp.getSession(inst);
612 if (s == null)
613 return;
614 UIEventListener uiEventListener = s.getUIEventListener();
615 if (uiEventListener != null)
616 uiEventListener.OnGraphicsUpdate(x, y, width, height);
617 }
618
619 private static void OnGraphicsResize(long inst, int width, int height, int bpp)
620 {
621 SessionState s = GlobalApp.getSession(inst);
622 if (s == null)
623 return;
624 UIEventListener uiEventListener = s.getUIEventListener();
625 if (uiEventListener != null)
626 uiEventListener.OnGraphicsResize(width, height, bpp);
627 }
628
629 private static void OnRemoteClipboardChanged(long inst, String data)
630 {
631 SessionState s = GlobalApp.getSession(inst);
632 if (s == null)
633 return;
634 UIEventListener uiEventListener = s.getUIEventListener();
635 if (uiEventListener != null)
636 uiEventListener.OnRemoteClipboardChanged(data);
637 }
638
639 public static String getVersion()
640 {
641 return freerdp_get_version();
642 }
643
644 public interface EventListener
645 {
646 void OnPreConnect(long instance);
647
648 void OnConnectionSuccess(long instance);
649
650 void OnConnectionFailure(long instance);
651
652 void OnDisconnecting(long instance);
653
654 void OnDisconnected(long instance);
655 }
656
657 public interface UIEventListener
658 {
659 void OnSettingsChanged(int width, int height, int bpp);
660
661 boolean OnAuthenticate(StringBuilder username, StringBuilder domain,
662 StringBuilder password);
663
664 boolean OnGatewayAuthenticate(StringBuilder username, StringBuilder domain,
665 StringBuilder password);
666
667 int OnVerifiyCertificateEx(String host, long port, String commonName, String subject, String issuer,
668 String fingerprint, long flags);
669
670 int OnVerifyChangedCertificateEx(String host, long port, String commonName, String subject, String issuer,
671 String fingerprint, String oldSubject, String oldIssuer,
672 String oldFingerprint, long flags);
673
674 void OnGraphicsUpdate(int x, int y, int width, int height);
675
676 void OnGraphicsResize(int width, int height, int bpp);
677
678 void OnRemoteClipboardChanged(String data);
679 }
680}