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
36UINT android_cliprdr_send_client_format_list(CliprdrClientContext* cliprdr)
37{
38 UINT rc = ERROR_INTERNAL_ERROR;
39 UINT32 formatId;
40 UINT32 numFormats;
41 UINT32* pFormatIds;
42 const char* formatName;
43 CLIPRDR_FORMAT* formats;
44 CLIPRDR_FORMAT_LIST formatList = WINPR_C_ARRAY_INIT;
45
46 if (!cliprdr)
47 return ERROR_INVALID_PARAMETER;
48
49 androidContext* afc = (androidContext*)cliprdr->custom;
50
51 if (!afc || !afc->cliprdr)
52 return ERROR_INVALID_PARAMETER;
53
54 pFormatIds = nullptr;
55 numFormats = ClipboardGetFormatIds(afc->clipboard, &pFormatIds);
56 formats = (CLIPRDR_FORMAT*)calloc(numFormats, sizeof(CLIPRDR_FORMAT));
57
58 if (!formats)
59 goto fail;
60
61 for (UINT32 index = 0; index < numFormats; index++)
62 {
63 formatId = pFormatIds[index];
64 formatName = ClipboardGetFormatName(afc->clipboard, formatId);
65 formats[index].formatId = formatId;
66 formats[index].formatName = nullptr;
67
68 if ((formatId > CF_MAX) && formatName)
69 {
70 formats[index].formatName = _strdup(formatName);
71
72 if (!formats[index].formatName)
73 goto fail;
74 }
75 }
76
77 formatList.common.msgFlags = 0;
78 formatList.numFormats = numFormats;
79 formatList.formats = formats;
80 formatList.common.msgType = CB_FORMAT_LIST;
81
82 if (!afc->cliprdr->ClientFormatList)
83 goto fail;
84
85 rc = afc->cliprdr->ClientFormatList(afc->cliprdr, &formatList);
86fail:
87 free(pFormatIds);
88 free(formats);
89 return rc;
90}
91
92static UINT android_cliprdr_send_client_format_data_request(CliprdrClientContext* cliprdr,
93 UINT32 formatId)
94{
95 UINT rc = ERROR_INVALID_PARAMETER;
96 CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = WINPR_C_ARRAY_INIT;
97 androidContext* afc;
98
99 if (!cliprdr)
100 goto fail;
101
102 afc = (androidContext*)cliprdr->custom;
103
104 if (!afc || !afc->clipboardRequestEvent || !cliprdr->ClientFormatDataRequest)
105 goto fail;
106
107 formatDataRequest.common.msgType = CB_FORMAT_DATA_REQUEST;
108 formatDataRequest.common.msgFlags = 0;
109 formatDataRequest.requestedFormatId = formatId;
110 afc->requestedFormatId = formatId;
111 (void)ResetEvent(afc->clipboardRequestEvent);
112 rc = cliprdr->ClientFormatDataRequest(cliprdr, &formatDataRequest);
113fail:
114 return rc;
115}
116
117static UINT android_cliprdr_send_client_capabilities(CliprdrClientContext* cliprdr)
118{
119 CLIPRDR_CAPABILITIES capabilities;
120 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
121
122 if (!cliprdr || !cliprdr->ClientCapabilities)
123 return ERROR_INVALID_PARAMETER;
124
125 capabilities.cCapabilitiesSets = 1;
126 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
127 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
128 generalCapabilitySet.capabilitySetLength = 12;
129 generalCapabilitySet.version = CB_CAPS_VERSION_2;
130 generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
131 return cliprdr->ClientCapabilities(cliprdr, &capabilities);
132}
133
139static UINT android_cliprdr_monitor_ready(CliprdrClientContext* cliprdr,
140 const CLIPRDR_MONITOR_READY* monitorReady)
141{
142 UINT rc;
143 androidContext* afc;
144
145 if (!cliprdr || !monitorReady)
146 return ERROR_INVALID_PARAMETER;
147
148 afc = (androidContext*)cliprdr->custom;
149
150 if (!afc)
151 return ERROR_INVALID_PARAMETER;
152
153 if ((rc = android_cliprdr_send_client_capabilities(cliprdr)) != CHANNEL_RC_OK)
154 return rc;
155
156 if ((rc = android_cliprdr_send_client_format_list(cliprdr)) != CHANNEL_RC_OK)
157 return rc;
158
159 afc->clipboardSync = TRUE;
160 return CHANNEL_RC_OK;
161}
162
168static UINT android_cliprdr_server_capabilities(CliprdrClientContext* cliprdr,
169 const CLIPRDR_CAPABILITIES* capabilities)
170{
171 CLIPRDR_CAPABILITY_SET* capabilitySet;
172 androidContext* afc;
173
174 if (!cliprdr || !capabilities)
175 return ERROR_INVALID_PARAMETER;
176
177 afc = (androidContext*)cliprdr->custom;
178
179 if (!afc)
180 return ERROR_INVALID_PARAMETER;
181
182 for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
183 {
184 capabilitySet = &(capabilities->capabilitySets[index]);
185
186 if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
187 (capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
188 {
189 CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet =
190 (CLIPRDR_GENERAL_CAPABILITY_SET*)capabilitySet;
191 afc->clipboardCapabilities = generalCapabilitySet->generalFlags;
192 break;
193 }
194 }
195
196 return CHANNEL_RC_OK;
197}
198
204static UINT android_cliprdr_server_format_list(CliprdrClientContext* cliprdr,
205 const CLIPRDR_FORMAT_LIST* formatList)
206{
207 UINT rc;
208 CLIPRDR_FORMAT* format;
209 androidContext* afc;
210
211 if (!cliprdr || !formatList)
212 return ERROR_INVALID_PARAMETER;
213
214 afc = (androidContext*)cliprdr->custom;
215
216 if (!afc)
217 return ERROR_INVALID_PARAMETER;
218
219 if (afc->serverFormats)
220 {
221 for (UINT32 index = 0; index < afc->numServerFormats; index++)
222 free(afc->serverFormats[index].formatName);
223
224 free(afc->serverFormats);
225 afc->serverFormats = nullptr;
226 afc->numServerFormats = 0;
227 }
228
229 if (formatList->numFormats < 1)
230 return CHANNEL_RC_OK;
231
232 afc->numServerFormats = formatList->numFormats;
233 afc->serverFormats = (CLIPRDR_FORMAT*)calloc(afc->numServerFormats, sizeof(CLIPRDR_FORMAT));
234
235 if (!afc->serverFormats)
236 return CHANNEL_RC_NO_MEMORY;
237
238 for (UINT32 index = 0; index < afc->numServerFormats; index++)
239 {
240 afc->serverFormats[index].formatId = formatList->formats[index].formatId;
241 afc->serverFormats[index].formatName = nullptr;
242
243 if (formatList->formats[index].formatName)
244 {
245 afc->serverFormats[index].formatName = _strdup(formatList->formats[index].formatName);
246
247 if (!afc->serverFormats[index].formatName)
248 return CHANNEL_RC_NO_MEMORY;
249 }
250 }
251
252 /* Text formats take priority over image formats. Request the best available text format
253 * first; if none is found, fall back to image. */
254 for (UINT32 index = 0; index < afc->numServerFormats; index++)
255 {
256 format = &(afc->serverFormats[index]);
257
258 if (format->formatId == CF_UNICODETEXT)
259 {
260 if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, CF_UNICODETEXT)) !=
261 CHANNEL_RC_OK)
262 return rc;
263
264 return CHANNEL_RC_OK;
265 }
266 else if (format->formatId == CF_TEXT)
267 {
268 if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, CF_TEXT)) !=
269 CHANNEL_RC_OK)
270 return rc;
271
272 return CHANNEL_RC_OK;
273 }
274 }
275
276 /* No text format found — request the first available image format. */
277 for (UINT32 index = 0; index < afc->numServerFormats; index++)
278 {
279 format = &(afc->serverFormats[index]);
280
281 if (format->formatId == CF_DIB || format->formatId == CF_DIBV5)
282 {
283 if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, format->formatId)) !=
284 CHANNEL_RC_OK)
285 return rc;
286
287 break;
288 }
289 }
290
291 return CHANNEL_RC_OK;
292}
293
299static UINT
300android_cliprdr_server_format_list_response(CliprdrClientContext* cliprdr,
301 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
302{
303 if (!cliprdr || !formatListResponse)
304 return ERROR_INVALID_PARAMETER;
305
306 return CHANNEL_RC_OK;
307}
308
314static UINT
315android_cliprdr_server_lock_clipboard_data(CliprdrClientContext* cliprdr,
316 const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
317{
318 if (!cliprdr || !lockClipboardData)
319 return ERROR_INVALID_PARAMETER;
320
321 return CHANNEL_RC_OK;
322}
323
329static UINT android_cliprdr_server_unlock_clipboard_data(
330 CliprdrClientContext* cliprdr, const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
331{
332 if (!cliprdr || !unlockClipboardData)
333 return ERROR_INVALID_PARAMETER;
334
335 return CHANNEL_RC_OK;
336}
337
343static UINT
344android_cliprdr_server_format_data_request(CliprdrClientContext* cliprdr,
345 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
346{
347 UINT rc;
348 BYTE* data;
349 UINT32 size;
350 UINT32 formatId;
351 CLIPRDR_FORMAT_DATA_RESPONSE response = WINPR_C_ARRAY_INIT;
352 androidContext* afc;
353
354 if (!cliprdr || !formatDataRequest || !cliprdr->ClientFormatDataResponse)
355 return ERROR_INVALID_PARAMETER;
356
357 afc = (androidContext*)cliprdr->custom;
358
359 if (!afc)
360 return ERROR_INVALID_PARAMETER;
361
362 formatId = formatDataRequest->requestedFormatId;
363 data = (BYTE*)ClipboardGetData(afc->clipboard, formatId, &size);
364 response.common.msgFlags = CB_RESPONSE_OK;
365 response.common.dataLen = size;
366 response.requestedFormatData = data;
367
368 if (!data)
369 {
370 response.common.msgFlags = CB_RESPONSE_FAIL;
371 response.common.dataLen = 0;
372 response.requestedFormatData = nullptr;
373 }
374
375 rc = cliprdr->ClientFormatDataResponse(cliprdr, &response);
376 free(data);
377 return rc;
378}
379
385static UINT
386android_cliprdr_server_format_data_response(CliprdrClientContext* cliprdr,
387 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
388{
389 CLIPRDR_FORMAT* format = nullptr;
390
391 if (!cliprdr || !formatDataResponse)
392 return ERROR_INVALID_PARAMETER;
393
394 androidContext* afc = (androidContext*)cliprdr->custom;
395
396 if (!afc)
397 return ERROR_INVALID_PARAMETER;
398
399 freerdp* instance = ((rdpContext*)afc)->instance;
400
401 if (!instance)
402 return ERROR_INVALID_PARAMETER;
403
404 for (UINT32 index = 0; index < afc->numServerFormats; index++)
405 {
406 if (afc->requestedFormatId == afc->serverFormats[index].formatId)
407 format = &(afc->serverFormats[index]);
408 }
409
410 if (!format)
411 {
412 (void)SetEvent(afc->clipboardRequestEvent);
413 return ERROR_INTERNAL_ERROR;
414 }
415
416 UINT32 formatId = format->formatId;
417 if (format->formatName)
418 formatId = ClipboardRegisterFormat(afc->clipboard, format->formatName);
419
420 uint32_t size = formatDataResponse->common.dataLen;
421
422 if (!ClipboardSetData(afc->clipboard, formatId, formatDataResponse->requestedFormatData, size))
423 return ERROR_INTERNAL_ERROR;
424
425 (void)SetEvent(afc->clipboardRequestEvent);
426
427 switch (formatId)
428 {
429 case CF_TEXT:
430 case CF_UNICODETEXT:
431 {
432 JNIEnv* env = nullptr;
433 UINT32 plainFormatId = ClipboardRegisterFormat(afc->clipboard, "text/plain");
434 char* data = (char*)ClipboardGetData(afc->clipboard, plainFormatId, &size);
435 if (!data)
436 break;
437 jboolean attached = jni_attach_thread(&env);
438 size = strnlen(data, size);
439 jstring jdata = jniNewStringUTF(env, data, size);
440 freerdp_callback("OnRemoteClipboardChanged", "(JLjava/lang/String;)V", (jlong)instance,
441 jdata);
442 (*env)->DeleteLocalRef(env, jdata);
443 free(data);
444
445 if (attached == JNI_TRUE)
446 jni_detach_thread();
447 }
448 break;
449 case CF_DIB:
450 case CF_DIBV5:
451 {
452 UINT32 pngFormatId = ClipboardRegisterFormat(afc->clipboard, "image/png");
453 BYTE* pngData = (BYTE*)ClipboardGetData(afc->clipboard, pngFormatId, &size);
454
455 if (pngData)
456 {
457 JNIEnv* env = nullptr;
458 jboolean attached = jni_attach_thread(&env);
459 jbyteArray jpngData = (*env)->NewByteArray(env, (jsize)size);
460
461 if (jpngData)
462 {
463 (*env)->SetByteArrayRegion(env, jpngData, 0, (jsize)size,
464 (const jbyte*)pngData);
465 freerdp_callback("OnRemoteClipboardImageChanged", "(J[B)V", (jlong)instance,
466 jpngData);
467 (*env)->DeleteLocalRef(env, jpngData);
468 }
469
470 free(pngData);
471
472 if (attached == JNI_TRUE)
473 jni_detach_thread();
474 }
475 }
476 break;
477 default:
478 break;
479 }
480
481 return CHANNEL_RC_OK;
482}
483
489static UINT android_cliprdr_server_file_contents_request(
490 CliprdrClientContext* cliprdr, const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
491{
492 if (!cliprdr || !fileContentsRequest)
493 return ERROR_INVALID_PARAMETER;
494
495 return CHANNEL_RC_OK;
496}
497
503static UINT android_cliprdr_server_file_contents_response(
504 CliprdrClientContext* cliprdr, const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
505{
506 if (!cliprdr || !fileContentsResponse)
507 return ERROR_INVALID_PARAMETER;
508
509 return CHANNEL_RC_OK;
510}
511
512BOOL android_cliprdr_init(androidContext* afc, CliprdrClientContext* cliprdr)
513{
514 wClipboard* clipboard;
515 HANDLE hevent;
516
517 if (!afc || !cliprdr)
518 return FALSE;
519
520 if (!(hevent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
521 return FALSE;
522
523 if (!(clipboard = ClipboardCreate()))
524 {
525 (void)CloseHandle(hevent);
526 return FALSE;
527 }
528
529 afc->cliprdr = cliprdr;
530 afc->clipboard = clipboard;
531 afc->clipboardRequestEvent = hevent;
532 cliprdr->custom = (void*)afc;
533 cliprdr->MonitorReady = android_cliprdr_monitor_ready;
534 cliprdr->ServerCapabilities = android_cliprdr_server_capabilities;
535 cliprdr->ServerFormatList = android_cliprdr_server_format_list;
536 cliprdr->ServerFormatListResponse = android_cliprdr_server_format_list_response;
537 cliprdr->ServerLockClipboardData = android_cliprdr_server_lock_clipboard_data;
538 cliprdr->ServerUnlockClipboardData = android_cliprdr_server_unlock_clipboard_data;
539 cliprdr->ServerFormatDataRequest = android_cliprdr_server_format_data_request;
540 cliprdr->ServerFormatDataResponse = android_cliprdr_server_format_data_response;
541 cliprdr->ServerFileContentsRequest = android_cliprdr_server_file_contents_request;
542 cliprdr->ServerFileContentsResponse = android_cliprdr_server_file_contents_response;
543 return TRUE;
544}
545
546BOOL android_cliprdr_uninit(androidContext* afc, CliprdrClientContext* cliprdr)
547{
548 if (!afc || !cliprdr)
549 return FALSE;
550
551 cliprdr->custom = nullptr;
552 afc->cliprdr = nullptr;
553 ClipboardDestroy(afc->clipboard);
554 (void)CloseHandle(afc->clipboardRequestEvent);
555 return TRUE;
556}