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