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
qandroidcacertificates.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 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 <QtFFmpegMediaPluginImpl/private/qffmpegcertificateutils_p.h>
7
8#include <QtCore/qdir.h>
9#include <QtCore/qfile.h>
10#include <QtCore/qjniarray.h>
11#include <QtCore/qjniobject.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtCore/qsavefile.h>
14#include <QtCore/qstandardpaths.h>
15
16#include <jni.h>
17
18#include <atomic>
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24Q_DECLARE_JNI_CLASS(QtSslCertificates, "org/qtproject/qt/android/multimedia/qffmpeg/QtSslCertificates")
25
26namespace QFFmpeg {
27
28Q_STATIC_LOGGING_CATEGORY(qLCAndroidCaCertificates, "qt.multimedia.ffmpeg.android.caCertificates");
29
31{
32 QList<QByteArray> derCertificates;
33
34 auto certificates =
35 QtJniTypes::QtSslCertificates::callStaticMethod<QJniArray<QJniArray<jbyte>>>(
36 "getCertificates");
37 if (!certificates.isValid())
38 return derCertificates;
39
40 derCertificates.reserve(certificates.size());
41 for (const auto &jCertificate : certificates)
42 derCertificates.append(jCertificate->toContainer());
43
44 return derCertificates;
45}
46
48{
49 static const QByteArray pemBundle = certificatesToPemBundle(fetchAndroidSystemCertificates());
50
51 if (pemBundle.isEmpty()) {
52 qCWarning(qLCAndroidCaCertificates) << "Could not obtain any system CA certificates";
53 return {};
54 }
55
56 const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
57 QString path = cacheDir + "/ffmpeg_ca_bundle.pem"_L1;
58
59 // A bundle left behind by a previous run may predate a system trust store update, so
60 // rewrite it once per process. After that, only rewrite it if it got evicted.
61 // Concurrent callers may both rewrite it, which is harmless: QSaveFile replaces the file
62 // atomically, so writers and readers never see a partially written file.
63 static std::atomic_bool writtenInThisProcess = false;
64 if (writtenInThisProcess.load(std::memory_order_relaxed) && QFile::exists(path))
65 return path;
66
67 QDir().mkpath(cacheDir);
68
69 QSaveFile file(path);
70 if (!file.open(QIODevice::WriteOnly) || file.write(pemBundle) != pemBundle.size()
71 || !file.commit()) {
72 qCWarning(qLCAndroidCaCertificates) << "Failed to write CA bundle to" << path;
73 return {};
74 }
75
76 writtenInThisProcess.store(true, std::memory_order_relaxed);
77 return path;
78}
79
80} // namespace QFFmpeg
81
82QT_END_NAMESPACE
static QList< QByteArray > fetchAndroidSystemCertificates()
Q_STATIC_LOGGING_CATEGORY(qLCAndroidCaCertificates, "qt.multimedia.ffmpeg.android.caCertificates")
QString androidFFmpegCaBundlePath()
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")