FreeRDP
Loading...
Searching...
No Matches
android_cliprdr.c
1
22#include <freerdp/config.h>
23
24#include <jni.h>
25
26#include <winpr/crt.h>
27#include <winpr/stream.h>
28
29#include <freerdp/client/channels.h>
30#include <freerdp/client/cliprdr.h>
31
32#include "android_cliprdr.h"
33#include "android_jni_utils.h"
34#include "android_jni_callback.h"
35
36static void android_cliprdr_free_formats(CLIPRDR_FORMAT* formats, UINT32 numFormats)
37{
38 if (!formats)
39 return;
40
41 for (UINT32 index = 0; index < numFormats; index++)
42 free(formats[index].formatName);
43
44 free(formats);
45}
46
47UINT android_cliprdr_send_client_format_list(CliprdrClientContext* cliprdr)
48{
49 UINT rc = ERROR_INTERNAL_ERROR;
50 UINT32 formatId;
51 UINT32 numFormats;
52 UINT32* pFormatIds;
53 const char* formatName;
54 CLIPRDR_FORMAT* formats;
55 CLIPRDR_FORMAT_LIST formatList = WINPR_C_ARRAY_INIT;
56
57 if (!cliprdr)
58 return ERROR_INVALID_PARAMETER;
59
60 androidContext* afc = (androidContext*)cliprdr->custom;
61
62 if (!afc || !afc->cliprdr)
63 return ERROR_INVALID_PARAMETER;
64
65 pFormatIds = nullptr;
66 numFormats = ClipboardGetFormatIds(afc->clipboard, &pFormatIds);
67 formats = (CLIPRDR_FORMAT*)calloc(numFormats, sizeof(CLIPRDR_FORMAT));
68
69 if (!formats)
70 goto fail;
71
72 for (UINT32 index = 0; index < numFormats; index++)
73 {
74 formatId = pFormatIds[index];
75 formatName = ClipboardGetFormatName(afc->clipboard, formatId);
76 formats[index].formatId = formatId;
77 formats[index].formatName = nullptr;
78
79 if ((formatId > CF_MAX) && formatName)
80 {
81 formats[index].formatName = _strdup(formatName);
82
83 if (!formats[index].formatName)
84 goto fail;
85 }
86 }
87
88 formatList.common.msgFlags = 0;
89 formatList.numFormats = numFormats;
90 formatList.formats = formats;
91 formatList.common.msgType = CB_FORMAT_LIST;
92
93 if (!afc->cliprdr->ClientFormatList)
94 goto fail;
95
96 rc = afc->cliprdr->ClientFormatList(afc->cliprdr, &formatList);
97fail:
98 free(pFormatIds);
99 android_cliprdr_free_formats(formats, numFormats);
100 return rc;
101}
102
103static UINT android_cliprdr_send_client_format_data_request(CliprdrClientContext* cliprdr,
104 UINT32 formatId)
105{
106 UINT rc = ERROR_INVALID_PARAMETER;
107 CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = WINPR_C_ARRAY_INIT;
108 androidContext* afc;
109
110 if (!cliprdr)
111 goto fail;
112
113 afc = (androidContext*)cliprdr->custom;
114
115 if (!afc || !afc->clipboardRequestEvent || !cliprdr->ClientFormatDataRequest)
116 goto fail;
117
118 formatDataRequest.common.msgType = CB_FORMAT_DATA_REQUEST;
119 formatDataRequest.common.msgFlags = 0;
120 formatDataRequest.requestedFormatId = formatId;
121 afc->requestedFormatId = formatId;
122 (void)ResetEvent(afc->clipboardRequestEvent);
123 rc = cliprdr->ClientFormatDataRequest(cliprdr, &formatDataRequest);
124fail:
125 return rc;
126}
127
128static UINT android_cliprdr_send_client_capabilities(CliprdrClientContext* cliprdr)
129{
130 CLIPRDR_CAPABILITIES capabilities;
131 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
132
133 if (!cliprdr || !cliprdr->ClientCapabilities)
134 return ERROR_INVALID_PARAMETER;
135
136 capabilities.cCapabilitiesSets = 1;
137 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
138 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
139 generalCapabilitySet.capabilitySetLength = 12;
140 generalCapabilitySet.version = CB_CAPS_VERSION_2;
141 generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
142 return cliprdr->ClientCapabilities(cliprdr, &capabilities);
143}
144
150static UINT android_cliprdr_monitor_ready(CliprdrClientContext* cliprdr,
151 const CLIPRDR_MONITOR_READY* monitorReady)
152{
153 UINT rc;
154 androidContext* afc;
155
156 if (!cliprdr || !monitorReady)
157 return ERROR_INVALID_PARAMETER;
158
159 afc = (androidContext*)cliprdr->custom;
160
161 if (!afc)
162 return ERROR_INVALID_PARAMETER;
163
164 if ((rc = android_cliprdr_send_client_capabilities(cliprdr)) != CHANNEL_RC_OK)
165 return rc;
166
167 if ((rc = android_cliprdr_send_client_format_list(cliprdr)) != CHANNEL_RC_OK)
168 return rc;
169
170 afc->clipboardSync = TRUE;
171 return CHANNEL_RC_OK;
172}
173
179static UINT android_cliprdr_server_capabilities(CliprdrClientContext* cliprdr,
180 const CLIPRDR_CAPABILITIES* capabilities)
181{
182 CLIPRDR_CAPABILITY_SET* capabilitySet;
183 androidContext* afc;
184
185 if (!cliprdr || !capabilities)
186 return ERROR_INVALID_PARAMETER;
187
188 afc = (androidContext*)cliprdr->custom;
189
190 if (!afc)
191 return ERROR_INVALID_PARAMETER;
192
193 for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
194 {
195 capabilitySet = &(capabilities->capabilitySets[index]);
196
197 if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
198 (capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
199 {
200 CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet =
201 (CLIPRDR_GENERAL_CAPABILITY_SET*)capabilitySet;
202 afc->clipboardCapabilities = generalCapabilitySet->generalFlags;
203 break;
204 }
205 }
206
207 return CHANNEL_RC_OK;
208}
209
210static UINT android_cliprdr_send_client_format_list_response(CliprdrClientContext* cliprdr,
211 BOOL status)
212{
213 CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = WINPR_C_ARRAY_INIT;
214
215 formatListResponse.common.msgType = CB_FORMAT_LIST_RESPONSE;
216 formatListResponse.common.msgFlags = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
217 formatListResponse.common.dataLen = 0;
218
219 if (!cliprdr || !cliprdr->ClientFormatListResponse)
220 return ERROR_INVALID_PARAMETER;
221
222 return cliprdr->ClientFormatListResponse(cliprdr, &formatListResponse);
223}
224
230static UINT android_cliprdr_server_format_list(CliprdrClientContext* cliprdr,
231 const CLIPRDR_FORMAT_LIST* formatList)
232{
233 UINT rc;
234 CLIPRDR_FORMAT* format;
235 androidContext* afc;
236
237 if (!cliprdr || !formatList)
238 return ERROR_INVALID_PARAMETER;
239
240 afc = (androidContext*)cliprdr->custom;
241
242 if (!afc)
243 return ERROR_INVALID_PARAMETER;
244
245 android_cliprdr_free_formats(afc->serverFormats, afc->numServerFormats);
246 afc->serverFormats = nullptr;
247 afc->numServerFormats = 0;
248
249 if (formatList->numFormats < 1)
250 return android_cliprdr_send_client_format_list_response(cliprdr, TRUE);
251
252 afc->numServerFormats = formatList->numFormats;
253 afc->serverFormats = (CLIPRDR_FORMAT*)calloc(afc->numServerFormats, sizeof(CLIPRDR_FORMAT));
254
255 if (!afc->serverFormats)
256 return CHANNEL_RC_NO_MEMORY;
257
258 for (UINT32 index = 0; index < afc->numServerFormats; index++)
259 {
260 afc->serverFormats[index].formatId = formatList->formats[index].formatId;
261 afc->serverFormats[index].formatName = nullptr;
262
263 if (formatList->formats[index].formatName)
264 {
265 afc->serverFormats[index].formatName = _strdup(formatList->formats[index].formatName);
266
267 if (!afc->serverFormats[index].formatName)
268 return CHANNEL_RC_NO_MEMORY;
269 }
270 }
271
272 if ((rc = android_cliprdr_send_client_format_list_response(cliprdr, TRUE)) != CHANNEL_RC_OK)
273 return rc;
274
275 /* Text formats take priority over image formats. Request the best available text format
276 * first; if none is found, fall back to image. */
277 for (UINT32 index = 0; index < afc->numServerFormats; index++)
278 {
279 format = &(afc->serverFormats[index]);
280
281 if (format->formatId == CF_UNICODETEXT)
282 {
283 if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, CF_UNICODETEXT)) !=
284 CHANNEL_RC_OK)
285 return rc;
286
287 return CHANNEL_RC_OK;
288 }
289 else if (format->formatId == CF_TEXT)
290 {
291 if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, CF_TEXT)) !=
292 CHANNEL_RC_OK)
293 return rc;
294
295 return CHANNEL_RC_OK;
296 }
297 }
298
299 /* No text format found — request the first available image format. */
300 for (UINT32 index = 0; index < afc->numServerFormats; index++)
301 {
302 format = &(afc->serverFormats[index]);
303
304 if (format->formatId == CF_DIB || format->formatId == CF_DIBV5)
305 {
306 if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, format->formatId)) !=
307 CHANNEL_RC_OK)
308 return rc;
309
310 break;
311 }
312 }
313
314 return CHANNEL_RC_OK;
315}
316
322static UINT
323android_cliprdr_server_format_list_response(CliprdrClientContext* cliprdr,
324 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
325{
326 if (!cliprdr || !formatListResponse)
327 return ERROR_INVALID_PARAMETER;
328
329 return CHANNEL_RC_OK;
330}
331
337static UINT
338android_cliprdr_server_lock_clipboard_data(CliprdrClientContext* cliprdr,
339 const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
340{
341 if (!cliprdr || !lockClipboardData)
342 return ERROR_INVALID_PARAMETER;
343
344 return CHANNEL_RC_OK;
345}
346
352static UINT android_cliprdr_server_unlock_clipboard_data(
353 CliprdrClientContext* cliprdr, const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
354{
355 if (!cliprdr || !unlockClipboardData)
356 return ERROR_INVALID_PARAMETER;
357
358 return CHANNEL_RC_OK;
359}
360
366static UINT
367android_cliprdr_server_format_data_request(CliprdrClientContext* cliprdr,
368 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
369{
370 UINT rc;
371 BYTE* data;
372 UINT32 size;
373 UINT32 formatId;
374 CLIPRDR_FORMAT_DATA_RESPONSE response = WINPR_C_ARRAY_INIT;
375 androidContext* afc;
376
377 if (!cliprdr || !formatDataRequest || !cliprdr->ClientFormatDataResponse)
378 return ERROR_INVALID_PARAMETER;
379
380 afc = (androidContext*)cliprdr->custom;
381
382 if (!afc)
383 return ERROR_INVALID_PARAMETER;
384
385 formatId = formatDataRequest->requestedFormatId;
386 data = (BYTE*)ClipboardGetData(afc->clipboard, formatId, &size);
387 response.common.msgFlags = CB_RESPONSE_OK;
388 response.common.dataLen = size;
389 response.requestedFormatData = data;
390
391 if (!data)
392 {
393 response.common.msgFlags = CB_RESPONSE_FAIL;
394 response.common.dataLen = 0;
395 response.requestedFormatData = nullptr;
396 }
397
398 rc = cliprdr->ClientFormatDataResponse(cliprdr, &response);
399 free(data);
400 return rc;
401}
402
408static UINT
409android_cliprdr_server_format_data_response(CliprdrClientContext* cliprdr,
410 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
411{
412 CLIPRDR_FORMAT* format = nullptr;
413
414 if (!cliprdr || !formatDataResponse)
415 return ERROR_INVALID_PARAMETER;
416
417 androidContext* afc = (androidContext*)cliprdr->custom;
418
419 if (!afc)
420 return ERROR_INVALID_PARAMETER;
421
422 freerdp* instance = ((rdpContext*)afc)->instance;
423
424 if (!instance)
425 return ERROR_INVALID_PARAMETER;
426
427 for (UINT32 index = 0; index < afc->numServerFormats; index++)
428 {
429 if (afc->requestedFormatId == afc->serverFormats[index].formatId)
430 format = &(afc->serverFormats[index]);
431 }
432
433 if (!format)
434 {
435 (void)SetEvent(afc->clipboardRequestEvent);
436 return ERROR_INTERNAL_ERROR;
437 }
438
439 UINT32 formatId = format->formatId;
440 if (format->formatName)
441 formatId = ClipboardRegisterFormat(afc->clipboard, format->formatName);
442
443 uint32_t size = formatDataResponse->common.dataLen;
444
445 if (!ClipboardSetData(afc->clipboard, formatId, formatDataResponse->requestedFormatData, size))
446 return ERROR_INTERNAL_ERROR;
447
448 (void)SetEvent(afc->clipboardRequestEvent);
449
450 switch (formatId)
451 {
452 case CF_TEXT:
453 case CF_UNICODETEXT:
454 {
455 JNIEnv* env = nullptr;
456 UINT32 plainFormatId = ClipboardRegisterFormat(afc->clipboard, "text/plain");
457 char* data = (char*)ClipboardGetData(afc->clipboard, plainFormatId, &size);
458 if (!data)
459 break;
460 jboolean attached = jni_attach_thread(&env);
461 size = strnlen(data, size);
462 jstring jdata = jniNewStringUTF(env, data, size);
463 freerdp_callback("OnRemoteClipboardChanged", "(JLjava/lang/String;)V", (jlong)instance,
464 jdata);
465 (*env)->DeleteLocalRef(env, jdata);
466 free(data);
467
468 if (attached == JNI_TRUE)
469 jni_detach_thread();
470 }
471 break;
472 case CF_DIB:
473 case CF_DIBV5:
474 {
475 UINT32 pngFormatId = ClipboardRegisterFormat(afc->clipboard, "image/png");
476 BYTE* pngData = (BYTE*)ClipboardGetData(afc->clipboard, pngFormatId, &size);
477
478 if (pngData)
479 {
480 JNIEnv* env = nullptr;
481 jboolean attached = jni_attach_thread(&env);
482 jbyteArray jpngData = (*env)->NewByteArray(env, (jsize)size);
483
484 if (jpngData)
485 {
486 (*env)->SetByteArrayRegion(env, jpngData, 0, (jsize)size,
487 (const jbyte*)pngData);
488 freerdp_callback("OnRemoteClipboardImageChanged", "(J[B)V", (jlong)instance,
489 jpngData);
490 (*env)->DeleteLocalRef(env, jpngData);
491 }
492
493 free(pngData);
494
495 if (attached == JNI_TRUE)
496 jni_detach_thread();
497 }
498 }
499 break;
500 default:
501 break;
502 }
503
504 return CHANNEL_RC_OK;
505}
506
512static UINT android_cliprdr_server_file_contents_request(
513 CliprdrClientContext* cliprdr, const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
514{
515 if (!cliprdr || !fileContentsRequest)
516 return ERROR_INVALID_PARAMETER;
517
518 return CHANNEL_RC_OK;
519}
520
526static UINT android_cliprdr_server_file_contents_response(
527 CliprdrClientContext* cliprdr, const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
528{
529 if (!cliprdr || !fileContentsResponse)
530 return ERROR_INVALID_PARAMETER;
531
532 return CHANNEL_RC_OK;
533}
534
535BOOL android_cliprdr_init(androidContext* afc, CliprdrClientContext* cliprdr)
536{
537 wClipboard* clipboard;
538 HANDLE hevent;
539
540 if (!afc || !cliprdr)
541 return FALSE;
542
543 if (!(hevent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
544 return FALSE;
545
546 if (!(clipboard = ClipboardCreate()))
547 {
548 (void)CloseHandle(hevent);
549 return FALSE;
550 }
551
552 afc->cliprdr = cliprdr;
553 afc->clipboard = clipboard;
554 afc->clipboardRequestEvent = hevent;
555 cliprdr->custom = (void*)afc;
556 cliprdr->MonitorReady = android_cliprdr_monitor_ready;
557 cliprdr->ServerCapabilities = android_cliprdr_server_capabilities;
558 cliprdr->ServerFormatList = android_cliprdr_server_format_list;
559 cliprdr->ServerFormatListResponse = android_cliprdr_server_format_list_response;
560 cliprdr->ServerLockClipboardData = android_cliprdr_server_lock_clipboard_data;
561 cliprdr->ServerUnlockClipboardData = android_cliprdr_server_unlock_clipboard_data;
562 cliprdr->ServerFormatDataRequest = android_cliprdr_server_format_data_request;
563 cliprdr->ServerFormatDataResponse = android_cliprdr_server_format_data_response;
564 cliprdr->ServerFileContentsRequest = android_cliprdr_server_file_contents_request;
565 cliprdr->ServerFileContentsResponse = android_cliprdr_server_file_contents_response;
566 return TRUE;
567}
568
569BOOL android_cliprdr_uninit(androidContext* afc, CliprdrClientContext* cliprdr)
570{
571 if (!afc || !cliprdr)
572 return FALSE;
573
574 cliprdr->custom = nullptr;
575 afc->cliprdr = nullptr;
576 ClipboardDestroy(afc->clipboard);
577 (void)CloseHandle(afc->clipboardRequestEvent);
578 return TRUE;
579}