Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidextras.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtCore/qbuffer.h>
8#include <QtCore/qdatastream.h>
9#include <QtCore/qjnienvironment.h>
10#include <QtCore/qvariant.h>
11#include <QtCore/qmutex.h>
12#include <QtCore/qtimer.h>
13#include <QtCore/qset.h>
14
15#if QT_CONFIG(permissions)
16#include <QtCore/qpromise.h>
17#endif
18
20
21using namespace Qt::StringLiterals;
22using namespace QtJniTypes;
23
25{
26public:
28 explicit QAndroidParcelPrivate(const QJniObject& parcel);
29
30 void writeData(const QByteArray &data) const;
31 void writeBinder(const QAndroidBinder &binder) const;
32 void writeFileDescriptor(int fd) const;
33
35 int readFileDescriptor() const;
36 QAndroidBinder readBinder() const;
37
38private:
39 friend class QAndroidBinder;
40 friend class QAndroidParcel;
41 QJniObject handle;
42};
43
45{
46 explicit FileDescriptor(int fd = -1)
47 : handle("java/io/FileDescriptor")
48 {
49 QJniEnvironment().checkAndClearExceptions();
50 handle.setField("descriptor", fd);
51 }
52
54};
55
57 : handle(QJniObject::callStaticObjectMethod("android/os/Parcel","obtain",
58 "()Landroid/os/Parcel;").object())
59{}
60
62 : handle(parcel)
63{}
64
65void QAndroidParcelPrivate::writeData(const QByteArray &data) const
66{
67 if (data.isEmpty())
68 return;
69
70 handle.callMethod<void>("writeByteArray", data);
71}
72
73void QAndroidParcelPrivate::writeBinder(const QAndroidBinder &binder) const
74{
75 QJniEnvironment().checkAndClearExceptions();
76 handle.callMethod<void>("writeStrongBinder", "(Landroid/os/IBinder;)V",
77 binder.handle().object());
78}
79
81{
82 QJniEnvironment().checkAndClearExceptions();
83 handle.callMethod<void>("writeFileDescriptor", "(Ljava/io/FileDescriptor;)V",
84 FileDescriptor(fd).handle.object());
85}
86
88{
89 QJniEnvironment().checkAndClearExceptions();
90 auto array = handle.callObjectMethod("createByteArray", "()[B");
91 QJniEnvironment env;
92 auto sz = env->GetArrayLength(jbyteArray(array.object()));
93 QByteArray res(sz, Qt::Initialization::Uninitialized);
94 env->GetByteArrayRegion(jbyteArray(array.object()), 0, sz,
95 reinterpret_cast<jbyte *>(res.data()));
96 return res;
97}
98
100{
101 QJniEnvironment().checkAndClearExceptions();
102 auto parcelFD = handle.callObjectMethod("readFileDescriptor",
103 "()Landroid/os/ParcelFileDescriptor;");
104 if (parcelFD.isValid())
105 return parcelFD.callMethod<jint>("getFd", "()I");
106 return -1;
107}
108
109QAndroidBinder QAndroidParcelPrivate::readBinder() const
110{
111 QJniEnvironment().checkAndClearExceptions();
112 auto strongBinder = handle.callObjectMethod("readStrongBinder", "()Landroid/os/IBinder;");
113 return QAndroidBinder(strongBinder.object());
114}
115
116/*!
117 \class QAndroidParcel
118 \inheaderfile QtCore/private/qandroidextras_p.h
119 \preliminary
120 \inmodule QtCorePrivate
121 \brief Wraps the most important methods of Android Parcel class.
122
123 The QAndroidParcel is a convenience class that wraps the most important
124 \l {https://developer.android.com/reference/android/os/Parcel.html}{Android Parcel}
125 methods.
126
127 \since 6.2
128*/
129
130/*!
131 Creates a new object.
132 */
133QAndroidParcel::QAndroidParcel()
134 : d(new QAndroidParcelPrivate())
135{
136}
137
138/*!
139 Wraps the \a parcel object.
140 */
141QAndroidParcel::QAndroidParcel(const QJniObject& parcel)
142 : d(new QAndroidParcelPrivate(parcel))
143{
144
145}
146
147QAndroidParcel::~QAndroidParcel()
148{
149}
150
151/*!
152 Writes the provided \a data as a byte array
153 */
154void QAndroidParcel::writeData(const QByteArray &data) const
155{
156 d->writeData(data);
157}
158
159/*!
160 Writes the provided \a value. The value is converted into a
161 QByteArray before is written.
162 */
163void QAndroidParcel::writeVariant(const QVariant &value) const
164{
165 QByteArray buff;
166 QDataStream stream(&buff, QIODevice::WriteOnly);
167 stream << value;
168 d->writeData(buff);
169}
170
171/*!
172 Writes a \a binder object. This is useful for a client to
173 send to a server a binder which can be used by the server callback the client.
174 */
175void QAndroidParcel::writeBinder(const QAndroidBinder &binder) const
176{
177 d->writeBinder(binder);
178}
179
180/*!
181 Writes the provided \a fd.
182 */
183void QAndroidParcel::writeFileDescriptor(int fd) const
184{
185 d->writeFileDescriptor(fd);
186}
187
188/*!
189 Returns the data as a QByteArray
190 */
191QByteArray QAndroidParcel::readData() const
192{
193 return d->readData();
194}
195
196/*!
197 Returns the data as a QVariant
198 */
199QVariant QAndroidParcel::readVariant() const
200{
201 QDataStream stream(d->readData());
202 QVariant res;
203 stream >> res;
204 return res;
205}
206
207/*!
208 Returns the binder as a QAndroidBinder
209 */
210QAndroidBinder QAndroidParcel::readBinder() const
211{
212 return d->readBinder();
213}
214
215/*!
216 Returns the file descriptor
217 */
218int QAndroidParcel::readFileDescriptor() const
219{
220 return d->readFileDescriptor();
221}
222
223/*!
224 The return value is useful to call other Java API which are not covered by this wrapper
225 */
226QJniObject QAndroidParcel::handle() const
227{
228 return d->handle;
229}
230
231
232
233/*!
234 \class QAndroidBinder
235 \inheaderfile QtCore/private/qandroidextras_p.h
236 \preliminary
237 \inmodule QtCorePrivate
238 \brief Wraps the most important methods of Android Binder class.
239
240 The QAndroidBinder is a convenience class that wraps the most important
241 \l {https://developer.android.com/reference/android/os/Binder.html}{Android Binder}
242 methods.
243
244 \since 6.2
245*/
246
247
248/*!
249 \enum QAndroidBinder::CallType
250
251 This enum is used with \l QAndroidBinder::transact() to describe the mode in which the
252 IPC call is performed.
253
254 \value Normal normal IPC, meaning that the caller waits the result from the callee
255 \value OneWay one-way IPC, meaning that the caller returns immediately, without waiting
256 for a result from the callee
257*/
258
259
261{
262public:
263 explicit QAndroidBinderPrivate(QAndroidBinder *binder)
264 : handle("org/qtproject/qt/android/extras/QtAndroidBinder", "(J)V", jlong(binder))
265 , m_isQtAndroidBinder(true)
266 {
267 QJniEnvironment().checkAndClearExceptions();
268 }
269
270 explicit QAndroidBinderPrivate(const QJniObject &binder)
271 : handle(binder), m_isQtAndroidBinder(false) {};
272 void setDeleteListener(const std::function<void()> &func) { m_deleteListener = func; }
274 {
275 if (m_isQtAndroidBinder) {
276 QJniEnvironment().checkAndClearExceptions();
277 handle.callMethod<void>("setId", "(J)V", jlong(0));
278 if (m_deleteListener)
279 m_deleteListener();
280 }
281 }
282
283private:
284 QJniObject handle;
285 std::function<void()> m_deleteListener;
286 bool m_isQtAndroidBinder;
287 friend class QAndroidBinder;
288};
289
290/*!
291 Creates a new object which can be used to perform IPC.
292
293 \sa onTransact, transact
294 */
295QAndroidBinder::QAndroidBinder()
296 : d(new QAndroidBinderPrivate(this))
297{
298}
299
300/*!
301 Creates a new object from the \a binder Java object.
302
303 \sa transact
304 */
305QAndroidBinder::QAndroidBinder(const QJniObject &binder)
306 : d(new QAndroidBinderPrivate(binder))
307{
308}
309
310QAndroidBinder::~QAndroidBinder()
311{
312}
313
314/*!
315 Default implementation is a stub that returns false.
316 The user should override this method to get the transact data from the caller.
317
318 The \a code is the action to perform.
319 The \a data is the marshaled data sent by the caller.\br
320 The \a reply is the marshaled data to be sent to the caller.\br
321 The \a flags are the additional operation flags.\br
322
323 \warning This method is called from Binder's thread which is different
324 from the thread that this object was created.
325
326 \sa transact
327 */
328bool QAndroidBinder::onTransact(int /*code*/, const QAndroidParcel &/*data*/,
329 const QAndroidParcel &/*reply*/, CallType /*flags*/)
330{
331 return false;
332}
333
334/*!
335 Performs an IPC call
336
337 The \a code is the action to perform. Should be between
338 \l {https://developer.android.com/reference/android/os/IBinder.html#FIRST_CALL_TRANSACTION}
339 {FIRST_CALL_TRANSACTION} and
340 \l {https://developer.android.com/reference/android/os/IBinder.html#LAST_CALL_TRANSACTION}
341 {LAST_CALL_TRANSACTION}.\br
342 The \a data is the marshaled data to send to the target.\br
343 The \a reply (if specified) is the marshaled data to be received from the target.
344 May be \b nullptr if you are not interested in the return value.\br
345 The \a flags are the additional operation flags.\br
346
347 \return true on success
348 */
349bool QAndroidBinder::transact(int code, const QAndroidParcel &data,
350 QAndroidParcel *reply, CallType flags) const
351{
352 QJniEnvironment().checkAndClearExceptions();
353 return d->handle.callMethod<jboolean>("transact",
354 "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z",
355 jint(code), data.d->handle.object(),
356 reply ? reply->d->handle.object() : nullptr,
357 jint(flags));
358}
359
360/*!
361 The return value is useful to call other Java API which are not covered by this wrapper
362 */
363QJniObject QAndroidBinder::handle() const
364{
365 return d->handle;
366}
367
368
369
370
371/*!
372 \class QAndroidServiceConnection
373 \inheaderfile QtCore/private/qandroidextras_p.h
374 \preliminary
375 \inmodule QtCorePrivate
376 \brief Wraps the most important methods of Android ServiceConnection class.
377
378 The QAndroidServiceConnection is a convenience abstract class which wraps the
379 \l {https://developer.android.com/reference/android/content/ServiceConnection.html}{AndroidServiceConnection}
380 interface.
381
382 It is useful when you perform a QtAndroidPrivate::bindService operation.
383
384 \since 6.2
385*/
386
387/*!
388 Creates a new object
389 */
390QAndroidServiceConnection::QAndroidServiceConnection()
391 : m_handle("org/qtproject/qt/android/extras/QtAndroidServiceConnection", "(J)V", jlong(this))
392{
393}
394
395/*!
396 Creates a new object from an existing \a serviceConnection.
397
398 It's useful when you have your own Java implementation.
399 Of course onServiceConnected()/onServiceDisconnected()
400 will not be called anymore.
401 */
402QAndroidServiceConnection::QAndroidServiceConnection(const QJniObject &serviceConnection)
403 : m_handle(serviceConnection)
404{
405}
406
407QAndroidServiceConnection::~QAndroidServiceConnection()
408{
409 m_handle.callMethod<void>("setId", "(J)V", jlong(this));
410}
411
412/*!
413 returns the underline QJniObject
414 */
415QJniObject QAndroidServiceConnection::handle() const
416{
417 return m_handle;
418}
419
420/*!
421 \fn void QAndroidServiceConnection::onServiceConnected(const QString &name, const QAndroidBinder &serviceBinder)
422
423 This notification is called when the client managed to connect to the service.
424 The \a name contains the server name, the \a serviceBinder is the binder that the client
425 uses to perform IPC operations.
426
427 \warning This method is called from Binder's thread which is different
428 from the thread that this object was created.
429
430 returns the underline QJniObject
431 */
432
433/*!
434 \fn void QAndroidServiceConnection::onServiceDisconnected(const QString &name)
435
436 Called when a connection to the Service has been lost.
437 The \a name parameter specifies which connectioen was lost.
438
439 \warning This method is called from Binder's thread which is different
440 from the thread that this object was created.
441
442 returns the underline QJniObject
443 */
444
445
446Q_CONSTINIT static QBasicAtomicInteger<uint> nextUniqueActivityRequestCode = Q_BASIC_ATOMIC_INITIALIZER(0);
447
448// Get a unique activity request code.
450{
451 constexpr uint ReservedForQtOffset = 0x1000; // Reserve all request codes under 0x1000 for Qt
452
453 const uint requestCodeBase = nextUniqueActivityRequestCode.fetchAndAddRelaxed(1);
454 if (requestCodeBase == uint(INT_MAX) - ReservedForQtOffset)
455 qWarning("Unique activity request code has wrapped. Unexpected behavior may occur.");
456
457 const int requestCode = static_cast<int>(requestCodeBase + ReservedForQtOffset);
458 return requestCode;
459}
460
462{
463public:
467
468 int globalRequestCode(int localRequestCode) const
469 {
470 const auto oldSize = localToGlobalRequestCode.size();
471 auto &e = localToGlobalRequestCode[localRequestCode];
472 if (localToGlobalRequestCode.size() != oldSize) {
473 // new entry, populate:
474 int globalRequestCode = uniqueActivityRequestCode();
475 e = globalRequestCode;
476 globalToLocalRequestCode[globalRequestCode] = localRequestCode;
477 }
478 return e;
479 }
480
481 bool handleActivityResult(jint requestCode, jint resultCode, jobject data)
482 {
483 const auto it = std::as_const(globalToLocalRequestCode).find(requestCode);
484 if (it != globalToLocalRequestCode.cend()) {
485 q->handleActivityResult(*it, resultCode, QJniObject(data));
486 return true;
487 }
488
489 return false;
490 }
491
492 static QAndroidActivityResultReceiverPrivate *get(QAndroidActivityResultReceiver *publicObject)
493 {
494 return publicObject->d.get();
495 }
496};
497
498/*!
499 \class QAndroidActivityResultReceiver
500 \inheaderfile QtCore/private/qandroidextras_p.h
501 \preliminary
502 \inmodule QtCorePrivate
503 \since 6.2
504 \brief Interface used for callbacks from onActivityResult() in the main Android activity.
505
506 Create a subclass of this class to be notified of the results when using the
507 \c QtAndroidPrivate::startActivity() and \c QtAndroidPrivate::startIntentSender() APIs.
508
509 */
510
511/*!
512 \internal
513*/
514QAndroidActivityResultReceiver::QAndroidActivityResultReceiver()
515 : d(new QAndroidActivityResultReceiverPrivate)
516{
517 d->q = this;
518 QtAndroidPrivate::registerActivityResultListener(d.get());
519}
520
521/*!
522 \internal
523*/
524QAndroidActivityResultReceiver::~QAndroidActivityResultReceiver()
525{
526 QtAndroidPrivate::unregisterActivityResultListener(d.get());
527}
528
529/*!
530 \fn void QAndroidActivityResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data)
531
532 Reimplement this function to get activity results after starting an activity using
533 either QtAndroidPrivate::startActivity() or QtAndroidPrivate::startIntentSender().
534 The \a receiverRequestCode is the request code unique to this receiver which was
535 originally passed to the startActivity() or startIntentSender() functions. The
536 \a resultCode is the result returned by the activity, and \a data is either null
537 or a Java object of the class android.content.Intent. Both the last to arguments
538 are identical to the arguments passed to onActivityResult().
539*/
540
541
542
544{
545public:
546 QAndroidServicePrivate(QAndroidService *service,
547 const std::function<QAndroidBinder*(const QAndroidIntent&)> &binder ={})
550 {
551 QTimer::singleShot(0,this, [this]{ QtAndroidPrivate::setOnBindListener(this);});
552 }
553
555 {
556 QMutexLocker lock(&m_bindersMutex);
557 while (!m_binders.empty()) {
558 auto it = m_binders.begin();
559 lock.unlock();
560 delete (*it);
561 lock.relock();
562 }
563 }
564
565 // OnBindListener interface
566 jobject onBind(jobject intent) override
567 {
568 auto qai = QAndroidIntent(intent);
569 auto binder = m_binder ? m_binder(qai) : m_service->onBind(qai);
570 if (binder) {
571 {
572 QMutexLocker lock(&m_bindersMutex);
573 binder->d->setDeleteListener([this, binder]{binderDestroied(binder);});
574 m_binders.insert(binder);
575 }
576 return binder->handle().object();
577 }
578 return nullptr;
579 }
580
581private:
582 void binderDestroied(QAndroidBinder* obj)
583 {
584 QMutexLocker lock(&m_bindersMutex);
585 m_binders.remove(obj);
586 }
587
588public:
590 std::function<QAndroidBinder *(const QAndroidIntent &)> m_binder;
593};
594
595/*!
596 \class QAndroidService
597 \inheaderfile QtCore/private/qandroidextras_p.h
598 \preliminary
599 \inmodule QtCorePrivate
600 \brief Wraps the most important methods of Android Service class.
601
602 The QAndroidService is a convenience class that wraps the most important
603 \l {https://developer.android.com/reference/android/app/Service.html}{Android Service}
604 methods.
605
606 \since 6.2
607*/
608
609
610/*!
611 \fn QAndroidService::QAndroidService(int &argc, char **argv)
612
613 Creates a new Android service, passing \a argc and \a argv as parameters.
614
615 //! Parameter \a flags is omitted in the documentation.
616
617 \sa QCoreApplication
618 */
619QAndroidService::QAndroidService(int &argc, char **argv, int flags)
620 : QCoreApplication (argc, argv, QtAndroidPrivate::acuqireServiceSetup(flags))
621 , d(new QAndroidServicePrivate{this})
622{
623}
624
625/*!
626 \fn QAndroidService::QAndroidService(int &argc, char **argv, const std::function<QAndroidBinder *(const QAndroidIntent &)> &binder)
627
628 Creates a new Android service, passing \a argc and \a argv as parameters.
629
630 \a binder is used to create a \l {QAndroidBinder}{binder} when needed.
631
632 //! Parameter \a flags is omitted in the documentation.
633
634 \sa QCoreApplication
635 */
636QAndroidService::QAndroidService(int &argc, char **argv,
637 const std::function<QAndroidBinder*(const QAndroidIntent&)> &binder,
638 int flags)
639 : QCoreApplication (argc, argv, QtAndroidPrivate::acuqireServiceSetup(flags))
640 , d(new QAndroidServicePrivate{this, binder})
641{
642}
643
644QAndroidService::~QAndroidService()
645{}
646
647/*!
648 The user must override this method and to return a binder.
649
650 The \a intent parameter contains all the caller information.
651
652 The returned binder is used by the caller to perform IPC calls.
653
654 \warning This method is called from Binder's thread which is different
655 from the thread that this object was created.
656
657 \sa QAndroidBinder::onTransact, QAndroidBinder::transact
658 */
659QAndroidBinder* QAndroidService::onBind(const QAndroidIntent &/*intent*/)
660{
661 return nullptr;
662}
663
664static jboolean onTransact(JNIEnv */*env*/, jclass /*cls*/, jlong id, jint code, jobject data,
665 jobject reply, jint flags)
666{
667 if (!id)
668 return false;
669
670 return reinterpret_cast<QAndroidBinder*>(id)->onTransact(
671 code, QAndroidParcel(data), QAndroidParcel(reply), QAndroidBinder::CallType(flags));
672}
673
674static void onServiceConnected(JNIEnv */*env*/, jclass /*cls*/, jlong id, jstring name,
675 jobject service)
676{
677 if (!id)
678 return;
679
680 return reinterpret_cast<QAndroidServiceConnection *>(id)->onServiceConnected(
681 QJniObject(name).toString(), QAndroidBinder(service));
682}
683
684static void onServiceDisconnected(JNIEnv */*env*/, jclass /*cls*/, jlong id, jstring name)
685{
686 if (!id)
687 return;
688
689 return reinterpret_cast<QAndroidServiceConnection *>(id)->onServiceDisconnected(
690 QJniObject(name).toString());
691}
692
693bool QtAndroidPrivate::registerExtrasNatives(QJniEnvironment &env)
694{
695 static const JNINativeMethod methods[] = {
696 {"onTransact", "(JILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void *)onTransact},
697 {"onServiceConnected", "(JLjava/lang/String;Landroid/os/IBinder;)V", (void *)onServiceConnected},
698 {"onServiceDisconnected", "(JLjava/lang/String;)V", (void *)onServiceDisconnected}
699 };
700
701 return env.registerNativeMethods("org/qtproject/qt/android/extras/QtNative", methods, 3);
702}
703
704/*!
705 \class QAndroidIntent
706 \inheaderfile QtCore/private/qandroidextras_p.h
707 \preliminary
708 \inmodule QtCorePrivate
709 \brief Wraps the most important methods of Android Intent class.
710
711 The QAndroidIntent is a convenience class that wraps the most important
712 \l {https://developer.android.com/reference/android/content/Intent.html}{Android Intent}
713 methods.
714
715 \since 6.2
716*/
717
718/*!
719 Create a new intent
720 */
721QAndroidIntent::QAndroidIntent()
722 : m_handle("android.content.Intent", "()V")
723{
724
725}
726
727QAndroidIntent::~QAndroidIntent()
728{}
729
730/*!
731 Wraps the provided \a intent java object.
732 */
733QAndroidIntent::QAndroidIntent(const QJniObject &intent)
734 : m_handle(intent)
735{
736}
737
738/*!
739 Creates a new intent and sets the provided \a action.
740 */
741QAndroidIntent::QAndroidIntent(const QString &action)
742 : m_handle("android.content.Intent", "(Ljava/lang/String;)V",
743 QJniObject::fromString(action).object())
744{
745 QJniEnvironment().checkAndClearExceptions();
746}
747
748/*!
749 Creates a new intent and sets the provided \a packageContext and the service \a className.
750 Example:
751 \code
752 auto serviceIntent = QAndroidIntent(QtAndroidPrivate::androidActivity().object(), "com.example.MyService");
753 \endcode
754
755 \sa QtAndroidPrivate::bindService
756 */
757QAndroidIntent::QAndroidIntent(const QJniObject &packageContext, const char *className)
758 : m_handle("android/content/Intent", "(Landroid/content/Context;Ljava/lang/Class;)V",
759 packageContext.object(), QJniEnvironment().findClass(className))
760{
761 QJniEnvironment().checkAndClearExceptions();
762}
763
764/*!
765 Sets the \a key with the \a data in the Intent extras
766 */
767void QAndroidIntent::putExtra(const QString &key, const QByteArray &data)
768{
769 m_handle.callMethod<QtJniTypes::Intent>("putExtra", key, data);
770}
771
772/*!
773 Returns the extra \a key data from the Intent extras
774 */
775QByteArray QAndroidIntent::extraBytes(const QString &key)
776{
777 return m_handle.callMethod<QByteArray>("getByteArrayExtra", key);
778}
779
780/*!
781 Sets the \a key with the \a value in the Intent extras.
782 */
783void QAndroidIntent::putExtra(const QString &key, const QVariant &value)
784{
785 QByteArray buff;
786 QDataStream stream(&buff, QIODevice::WriteOnly);
787 stream << value;
788 putExtra(key, buff);
789}
790
791/*!
792 Returns the extra \a key data from the Intent extras as a QVariant
793 */
794QVariant QAndroidIntent::extraVariant(const QString &key)
795{
796 QDataStream stream(extraBytes(key));
797 QVariant res;
798 stream >> res;
799 return res;
800}
801
802/*!
803 The return value is useful to call other Java API which are not covered by this wrapper
804 */
805QJniObject QAndroidIntent::handle() const
806{
807 return m_handle;
808}
809
810
811
812/*!
813 \namespace QtAndroidPrivate
814 \preliminary
815 \inmodule QtCorePrivate
816 \since 6.2
817 \brief The QtAndroidPrivate namespace provides miscellaneous functions
818 to aid Android development.
819 \inheaderfile QtCore/private/qandroidextras_p.h
820*/
821
822/*!
823 \since 6.2
824 \enum QtAndroidPrivate::BindFlag
825
826 This enum is used with QtAndroidPrivate::bindService to describe the mode in which the
827 binding is performed.
828
829 \value None No options.
830 \value AutoCreate Automatically creates the service as long as the binding exist.
831 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_AUTO_CREATE}
832 {BIND_AUTO_CREATE} documentation for more details.
833 \value DebugUnbind Include debugging help for mismatched calls to unbind.
834 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_DEBUG_UNBIND}
835 {BIND_DEBUG_UNBIND} documentation for more details.
836 \value NotForeground Don't allow this binding to raise the target service's process to the foreground scheduling priority.
837 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_NOT_FOREGROUND}
838 {BIND_NOT_FOREGROUND} documentation for more details.
839 \value AboveClient Indicates that the client application binding to this service considers the service to be more important than the app itself.
840 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_ABOVE_CLIENT}
841 {BIND_ABOVE_CLIENT} documentation for more details.
842 \value AllowOomManagement Allow the process hosting the bound service to go through its normal memory management.
843 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_ALLOW_OOM_MANAGEMENT}
844 {BIND_ALLOW_OOM_MANAGEMENT} documentation for more details.
845 \value WaivePriority Don't impact the scheduling or memory management priority of the target service's hosting process.
846 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_WAIVE_PRIORITY}
847 {BIND_WAIVE_PRIORITY} documentation for more details.
848 \value Important This service is assigned a higher priority so that it is available to the client when needed.
849 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_IMPORTANT}
850 {BIND_IMPORTANT} documentation for more details.
851 \value AdjustWithActivity If binding from an activity, allow the target service's process importance to be raised based on whether the activity is visible to the user.
852 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_ADJUST_WITH_ACTIVITY}
853 {BIND_ADJUST_WITH_ACTIVITY} documentation for more details.
854 \value ExternalService The service being bound is an isolated, external service.
855 See \l {https://developer.android.com/reference/android/content/Context.html#BIND_EXTERNAL_SERVICE}
856 {BIND_EXTERNAL_SERVICE} documentation for more details.
857*/
858
859/*!
860 \since 6.2
861
862 Starts the activity given by \a intent and provides the result asynchronously through the
863 \a resultReceiver if this is non-null.
864
865 If \a resultReceiver is null, then the \c startActivity() method of
866 QNativeInterface::QAndroidApplication::context() will be called. Otherwise
867 \c startActivityForResult() will be called.
868
869 The \a receiverRequestCode is a request code unique to the \a resultReceiver, and will be
870 returned along with the result, making it possible to use the same receiver for more than
871 one intent.
872
873 */
874void QtAndroidPrivate::startActivity(const QJniObject &intent,
875 int receiverRequestCode,
876 QAndroidActivityResultReceiver *resultReceiver)
877{
878 QJniObject activity = QtAndroidPrivate::activity();
879 if (resultReceiver != 0) {
881 QAndroidActivityResultReceiverPrivate::get(resultReceiver);
882 activity.callMethod<void>("startActivityForResult",
883 "(Landroid/content/Intent;I)V",
884 intent.object<jobject>(),
885 resultReceiverD->globalRequestCode(receiverRequestCode));
886 } else {
887 activity.callMethod<void>("startActivity",
888 "(Landroid/content/Intent;)V",
889 intent.object<jobject>());
890 }
891}
892
893/*!
894 \since 6.2
895
896 Starts the activity given by \a intent and provides the result asynchronously through the
897 \a resultReceiver if this is non-null.
898
899 If \a resultReceiver is null, then the \c startActivity() method of
900 QNativeInterface::QAndroidApplication::context() will be called. Otherwise
901 \c startActivityForResult() will be called.
902
903 The \a receiverRequestCode is a request code unique to the \a resultReceiver, and will be
904 returned along with the result, making it possible to use the same receiver for more than
905 one intent.
906
907 */
908void QtAndroidPrivate::startActivity(const QAndroidIntent &intent,
909 int receiverRequestCode,
910 QAndroidActivityResultReceiver *resultReceiver)
911{
912 startActivity(intent.handle(), receiverRequestCode, resultReceiver);
913}
914
915/*!
916 \since 6.2
917
918 Starts the activity given by \a intent, using the request code \a receiverRequestCode,
919 and provides the result by calling \a callbackFunc.
920*/
921void QtAndroidPrivate::startActivity(const QJniObject &intent,
922 int receiverRequestCode,
923 std::function<void(int, int, const QJniObject &data)> callbackFunc)
924{
925 QJniObject activity = QtAndroidPrivate::activity();
927 callbackFunc);
928 startActivity(intent, receiverRequestCode, QAndroidActivityCallbackResultReceiver::instance());
929}
930
931/*!
932 \since 6.2
933
934 Starts the activity given by \a intentSender and provides the result asynchronously through the
935 \a resultReceiver if this is non-null.
936
937 If \a resultReceiver is null, then the \c startIntentSender() method of
938 QNativeInterface::QAndroidApplication::context() will be called. Otherwise
939 \c startIntentSenderForResult() will be called.
940
941 The \a receiverRequestCode is a request code unique to the \a resultReceiver, and will be
942 returned along with the result, making it possible to use the same receiver for more than
943 one intent.
944
945*/
946void QtAndroidPrivate::startIntentSender(const QJniObject &intentSender,
947 int receiverRequestCode,
948 QAndroidActivityResultReceiver *resultReceiver)
949{
950 QJniObject activity = QtAndroidPrivate::activity();
951 if (resultReceiver != 0) {
953 QAndroidActivityResultReceiverPrivate::get(resultReceiver);
954 activity.callMethod<void>("startIntentSenderForResult",
955 "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V",
956 intentSender.object<jobject>(),
957 resultReceiverD->globalRequestCode(receiverRequestCode),
958 0, // fillInIntent
959 0, // flagsMask
960 0, // flagsValues
961 0); // extraFlags
962 } else {
963 activity.callMethod<void>("startIntentSender",
964 "(Landroid/content/IntentSender;Landroid/content/Intent;III)V",
965 intentSender.object<jobject>(),
966 0, // fillInIntent
967 0, // flagsMask
968 0, // flagsValues
969 0); // extraFlags
970
971 }
972
973}
974
975/*!
976 \since 6.2
977 \fn bool QtAndroidPrivate::bindService(const QAndroidIntent &serviceIntent, const QAndroidServiceConnection &serviceConnection, BindFlags flags = BindFlag::None)
978
979 Binds the service given by \a serviceIntent, \a serviceConnection and \a flags.
980 The \a serviceIntent object identifies the service to connect to.
981 The \a serviceConnection is a listener that receives the information as the service
982 is started and stopped.
983
984 \return true on success
985
986 See \l {https://developer.android.com/reference/android/content/Context.html#bindService%28android.content.Intent,%20android.content.ServiceConnection,%20int%29}
987 {Android documentation} documentation for more details.
988
989 \sa QAndroidIntent, QAndroidServiceConnection, BindFlag
990*/
991bool QtAndroidPrivate::bindService(const QAndroidIntent &serviceIntent,
992 const QAndroidServiceConnection &serviceConnection, BindFlags flags)
993{
994 QJniEnvironment().checkAndClearExceptions();
995 QJniObject contextObj = QtAndroidPrivate::context();
996 return contextObj.callMethod<jboolean>(
997 "bindService",
998 "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z",
999 serviceIntent.handle().object(),
1000 serviceConnection.handle().object(),
1001 jint(flags));
1002}
1003
1005
1011
1013 int resultCode,
1014 const QJniObject &intent)
1015{
1016 callbackMap[receiverRequestCode](receiverRequestCode, resultCode, intent);
1017 callbackMap.remove(receiverRequestCode);
1018}
1019
1021 if (!s_instance) {
1023 }
1024 return s_instance;
1025}
1026
1028 int receiverRequestCode,
1029 std::function<void(int, int, const QJniObject &data)> callbackFunc)
1030{
1031 callbackMap.insert(receiverRequestCode, callbackFunc);
1032}
1033
1034#if QT_CONFIG(permissions)
1035// Permissions API
1036
1037QtAndroidPrivate::PermissionResult resultFromAndroid(jint value)
1038{
1039 return value == 0 ? QtAndroidPrivate::Authorized : QtAndroidPrivate::Denied;
1040}
1041
1042using PendingPermissionRequestsHash
1043 = QHash<int, QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>>>;
1044Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests);
1045Q_CONSTINIT static QBasicMutex g_pendingPermissionRequestsMutex;
1046
1047static int nextRequestCode()
1048{
1049 Q_CONSTINIT static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
1050 return counter.fetchAndAddRelaxed(1);
1051}
1052
1053/*!
1054 \internal
1055
1056 This function is called when the result of the permission request is available.
1057 Once a permission is requested, the result is broadcast by the OS and listened
1058 to by QtActivity which passes it to C++ through a native JNI method call.
1059 */
1060static void sendRequestPermissionsResult(JNIEnv *env, jclass obj, jint requestCode,
1061 const QJniArray<int> &grantResults)
1062{
1063 Q_UNUSED(env);
1064 Q_UNUSED(obj);
1065
1066 QMutexLocker locker(&g_pendingPermissionRequestsMutex);
1067 auto it = g_pendingPermissionRequests->constFind(requestCode);
1068 if (it == g_pendingPermissionRequests->constEnd()) {
1069 qWarning() << "Found no valid pending permission request for request code" << requestCode;
1070 return;
1071 }
1072
1073 auto request = *it;
1074 g_pendingPermissionRequests->erase(it);
1075 locker.unlock();
1076
1077 request->addResults([grantResults](){
1078 QList<QtAndroidPrivate::PermissionResult> results(grantResults.size(),
1079 Qt::Uninitialized);
1080 for (qsizetype i = 0; i < grantResults.size(); ++i)
1081 results[i] = resultFromAndroid(grantResults.at(i));
1082 return results;
1083 }());
1084
1085 QtAndroidPrivate::releaseAndroidDeadlockProtector();
1086 request->finish();
1087}
1088Q_DECLARE_JNI_NATIVE_METHOD(sendRequestPermissionsResult)
1089
1090QFuture<QtAndroidPrivate::PermissionResult>
1091requestPermissionsInternal(const QStringList &permissions)
1092{
1093 // No mechanism to request permission for SDK version below 23, because
1094 // permissions defined in the manifest are granted at install time.
1095 if (QtAndroidPrivate::androidSdkVersion() < 23) {
1096 QList<QtAndroidPrivate::PermissionResult> result;
1097 result.reserve(permissions.size());
1098 // ### can we kick off all checkPermission()s, and whenAll() collect results?
1099 for (const QString &permission : permissions)
1100 result.push_back(QtAndroidPrivate::checkPermission(permission).result());
1101 return QtFuture::makeReadyRangeFuture(result);
1102 }
1103
1104 QtAndroidPrivate::AndroidDeadlockProtector protector(
1105 u"requestPermissionsInternal()"_s);
1106 if (!protector.acquire())
1107 return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
1108
1109 QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise;
1110 promise.reset(new QPromise<QtAndroidPrivate::PermissionResult>());
1111 QFuture<QtAndroidPrivate::PermissionResult> future = promise->future();
1112 promise->start();
1113
1114 const int requestCode = nextRequestCode();
1115 QMutexLocker locker(&g_pendingPermissionRequestsMutex);
1116 g_pendingPermissionRequests->insert(requestCode, promise);
1117 locker.unlock();
1118
1119 QNativeInterface::QAndroidApplication::runOnAndroidMainThread([permissions, requestCode] {
1120 QJniEnvironment env;
1121 jclass clazz = env.findClass("java/lang/String");
1122 auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
1123 int index = 0;
1124
1125 for (auto &perm : permissions)
1126 env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object());
1127
1128 QJniObject(QtAndroidPrivate::activity()).callMethod<void>("requestPermissions",
1129 "([Ljava/lang/String;I)V",
1130 array,
1131 requestCode);
1132 env->DeleteLocalRef(array);
1133 });
1134
1135 QtAndroidPrivate::releaseAndroidDeadlockProtector();
1136
1137 return future;
1138}
1139
1140/*!
1141 \preliminary
1142 Requests the \a permission and returns a QFuture representing the
1143 result of the request.
1144
1145 \note QPermission is the recommended API to use for requesting permissions.
1146 If QPermission doesn't cover an Android permission you want to request,
1147 this preliminary API can still used instead.
1148
1149 \since 6.2
1150 \sa checkPermission()
1151*/
1152QFuture<QtAndroidPrivate::PermissionResult>
1153QtAndroidPrivate::requestPermission(const QString &permission)
1154{
1155 return requestPermissions({permission});
1156}
1157
1158QFuture<QtAndroidPrivate::PermissionResult>
1159QtAndroidPrivate::requestPermissions(const QStringList &permissions)
1160{
1161 // avoid the uneccessary call and response to an empty permission string
1162 if (permissions.isEmpty())
1163 return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
1164 return requestPermissionsInternal(permissions);
1165}
1166
1167/*!
1168 \preliminary
1169 Checks whether this process has the named \a permission and returns a QFuture
1170 representing the result of the check.
1171
1172 \note QPermission is the recommended API to use for requesting permissions.
1173 If QPermission doesn't cover an Android permission you want to request,
1174 this preliminary API can still used instead.
1175
1176 \since 6.2
1177 \sa requestPermission()
1178*/
1179QFuture<QtAndroidPrivate::PermissionResult>
1180QtAndroidPrivate::checkPermission(const QString &permission)
1181{
1182 QtAndroidPrivate::PermissionResult result = Denied;
1183 if (!permission.isEmpty()) {
1184 auto res = QtNative::callStaticMethod<jint>("checkSelfPermission", permission);
1185 result = resultFromAndroid(res);
1186 }
1187 return QtFuture::makeReadyValueFuture(result);
1188}
1189
1190bool QtAndroidPrivate::registerPermissionNatives(QJniEnvironment &env)
1191{
1192 if (QtAndroidPrivate::androidSdkVersion() < 23)
1193 return true;
1194
1195 return env.registerNativeMethods<QtNative>({
1196 Q_JNI_NATIVE_METHOD(sendRequestPermissionsResult)
1197 });
1198}
1199
1200#endif // QT_CONFIG(permissions)
1201
1202QT_END_NAMESPACE
1203
1204#include "moc_qandroidextras_p.cpp"
void handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &intent) override
Reimplement this function to get activity results after starting an activity using either QtAndroidPr...
static QAndroidActivityCallbackResultReceiver * instance()
void registerCallback(int receiverRequestCode, std::function< void(int, int, const QJniObject &)> callbackFunc)
int globalRequestCode(int localRequestCode) const
QAndroidActivityResultReceiver * q
bool handleActivityResult(jint requestCode, jint resultCode, jobject data)
static QAndroidActivityResultReceiverPrivate * get(QAndroidActivityResultReceiver *publicObject)
QAndroidBinderPrivate(const QJniObject &binder)
void setDeleteListener(const std::function< void()> &func)
QAndroidBinderPrivate(QAndroidBinder *binder)
void writeBinder(const QAndroidBinder &binder) const
QAndroidBinder readBinder() const
void writeData(const QByteArray &data) const
QAndroidParcelPrivate(const QJniObject &parcel)
QByteArray readData() const
void writeFileDescriptor(int fd) const
QSet< QAndroidBinder * > m_binders
std::function< QAndroidBinder *(const QAndroidIntent &)> m_binder
QAndroidServicePrivate(QAndroidService *service, const std::function< QAndroidBinder *(const QAndroidIntent &)> &binder={})
QAndroidService * m_service
jobject onBind(jobject intent) override
Combined button and popup list for selecting options.
\preliminary \inmodule QtCorePrivate
Q_CORE_EXPORT void startIntentSender(const QJniObject &intentSender, int receiverRequestCode, QAndroidActivityResultReceiver *resultReceiver=nullptr)
bool registerExtrasNatives(QJniEnvironment &env)
Q_CORE_EXPORT void startActivity(const QJniObject &intent, int receiverRequestCode, QAndroidActivityResultReceiver *resultReceiver=nullptr)
Q_CORE_EXPORT bool bindService(const QAndroidIntent &serviceIntent, const QAndroidServiceConnection &serviceConnection, BindFlags flags=BindFlag::None)
Q_CORE_EXPORT void startActivity(const QJniObject &intent, int receiverRequestCode, std::function< void(int, int, const QJniObject &data)> callbackFunc)
static jboolean onTransact(JNIEnv *, jclass, jlong id, jint code, jobject data, jobject reply, jint flags)
static void onServiceConnected(JNIEnv *, jclass, jlong id, jstring name, jobject service)
static void onServiceDisconnected(JNIEnv *, jclass, jlong id, jstring name)
static int uniqueActivityRequestCode()
FileDescriptor(int fd=-1)