1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "androidconnectivitymanager.h"
41 #include <QtCore/private/qjni_p.h>
42 #include <QtCore/private/qjnihelpers_p.h>
43 
44 QT_BEGIN_NAMESPACE
45 
exceptionCheckAndClear(JNIEnv * env)46 static inline bool exceptionCheckAndClear(JNIEnv *env)
47 {
48     if (!env->ExceptionCheck())
49         return false;
50 
51 #ifdef QT_DEBUG
52     env->ExceptionDescribe();
53 #endif // QT_DEBUG
54     env->ExceptionClear();
55 
56     return true;
57 }
58 
59 struct AndroidConnectivityManagerInstance
60 {
AndroidConnectivityManagerInstanceAndroidConnectivityManagerInstance61     AndroidConnectivityManagerInstance()
62         : connManager(new AndroidConnectivityManager)
63     {    }
~AndroidConnectivityManagerInstanceAndroidConnectivityManagerInstance64     ~AndroidConnectivityManagerInstance()
65     {
66         delete connManager;
67     }
68 
69     AndroidConnectivityManager* connManager;
70 };
71 
72 Q_GLOBAL_STATIC(AndroidConnectivityManagerInstance, androidConnManagerInstance)
73 
74 static const char networkReceiverClass[] = "org/qtproject/qt5/android/bearer/QtNetworkReceiver";
75 static const char trafficStatsClass[] = "android/net/TrafficStats";
76 
77 /**
78  * Returns the number of bytes transmitted over the mobile network since last device boot.
79  */
getMobileTxBytes()80 qint64 AndroidTrafficStats::getMobileTxBytes()
81 {
82     return QJNIObjectPrivate::callStaticMethod<jlong>(trafficStatsClass,
83                                                       "getMobileTxBytes",
84                                                       "()J");
85 }
86 
87 /**
88  * Returns the number of bytes received over the mobile network since last device boot.
89  */
getMobileRxBytes()90 qint64 AndroidTrafficStats::getMobileRxBytes()
91 {
92     return QJNIObjectPrivate::callStaticMethod<jlong>(trafficStatsClass,
93                                                       "getMobileRxBytes",
94                                                       "()J");
95 }
96 
97 /**
98  * Returns the total transmitted bytes since last device boot.
99  */
getTotalTxBytes()100 qint64 AndroidTrafficStats::getTotalTxBytes()
101 {
102     return QJNIObjectPrivate::callStaticMethod<jlong>(trafficStatsClass,
103                                                       "getTotalTxBytes",
104                                                       "()J");
105 }
106 
107 /**
108  * Returns the total received bytes since last device boot.
109  */
getTotalRxBytes()110 qint64 AndroidTrafficStats::getTotalRxBytes()
111 {
112     return QJNIObjectPrivate::callStaticMethod<jlong>(trafficStatsClass,
113                                                       "getTotalRxBytes",
114                                                       "()J");
115 }
116 
isTrafficStatsSupported()117 bool AndroidTrafficStats::isTrafficStatsSupported()
118 {
119     // Before API level 18 DataStatistics might not be supported, so make sure that we get something
120     // else then -1 from from getXXBytes().
121     return (AndroidTrafficStats::getMobileRxBytes() != -1
122             && AndroidTrafficStats::getTotalRxBytes() != -1);
123 }
124 
stateForName(const QString & stateName)125 static AndroidNetworkInfo::NetworkState stateForName(const QString &stateName)
126 {
127     if (stateName == QLatin1String("CONNECTED"))
128         return AndroidNetworkInfo::Connected;
129     else if (stateName == QLatin1String("CONNECTING"))
130         return AndroidNetworkInfo::Connecting;
131     else if (stateName == QLatin1String("DISCONNECTED"))
132         return AndroidNetworkInfo::Disconnected;
133     else if (stateName == QLatin1String("DISCONNECTING"))
134         return AndroidNetworkInfo::Disconnecting;
135     else if (stateName == QLatin1String("SUSPENDED"))
136         return AndroidNetworkInfo::Suspended;
137 
138     return AndroidNetworkInfo::UnknownState;
139 }
140 
getDetailedState() const141 AndroidNetworkInfo::NetworkState AndroidNetworkInfo::getDetailedState() const
142 {
143     QJNIObjectPrivate enumObject = m_networkInfo.callObjectMethod("getDetailedState",
144                                                                   "()Landroid/net/NetworkInfo$DetailedState;");
145     if (!enumObject.isValid())
146         return UnknownState;
147 
148     QJNIObjectPrivate enumName = enumObject.callObjectMethod<jstring>("name");
149     if (!enumName.isValid())
150         return UnknownState;
151 
152     return stateForName(enumName.toString());
153 }
154 
getExtraInfo() const155 QString AndroidNetworkInfo::getExtraInfo() const
156 {
157     QJNIObjectPrivate extraInfo = m_networkInfo.callObjectMethod<jstring>("getExtraInfo");
158     if (!extraInfo.isValid())
159         return QString();
160 
161     return extraInfo.toString();
162 }
163 
getReason() const164 QString AndroidNetworkInfo::getReason() const
165 {
166     QJNIObjectPrivate reason = m_networkInfo.callObjectMethod<jstring>("getReason");
167     if (!reason.isValid())
168         return QString();
169 
170     return reason.toString();
171 }
172 
getState() const173 AndroidNetworkInfo::NetworkState AndroidNetworkInfo::getState() const
174 {
175     QJNIObjectPrivate enumObject = m_networkInfo.callObjectMethod("getState",
176                                                                   "()Landroid/net/NetworkInfo$State;");
177     if (!enumObject.isValid())
178         return UnknownState;
179 
180     QJNIObjectPrivate enumName = enumObject.callObjectMethod<jstring>("name");
181     if (!enumName.isValid())
182         return UnknownState;
183 
184     return stateForName(enumName.toString());
185 }
186 
getSubtype() const187 AndroidNetworkInfo::NetworkSubType AndroidNetworkInfo::getSubtype() const
188 {
189     return AndroidNetworkInfo::NetworkSubType(m_networkInfo.callMethod<jint>("getSubtype"));
190 }
191 
getSubtypeName() const192 QString AndroidNetworkInfo::getSubtypeName() const
193 {
194     QJNIObjectPrivate subtypeName = m_networkInfo.callObjectMethod<jstring>("getSubtypeName");
195     if (!subtypeName.isValid())
196         return QString();
197 
198     return subtypeName.toString();
199 }
200 
getType() const201 AndroidNetworkInfo::NetworkType AndroidNetworkInfo::getType() const
202 {
203     return AndroidNetworkInfo::NetworkType(m_networkInfo.callMethod<jint>("getType"));
204 }
205 
getTypeName() const206 QString AndroidNetworkInfo::getTypeName() const
207 {
208     QJNIObjectPrivate typeName = m_networkInfo.callObjectMethod<jstring>("getTypeName");
209     if (!typeName.isValid())
210         return QString();
211 
212     return typeName.toString();
213 }
214 
isAvailable() const215 bool AndroidNetworkInfo::isAvailable() const
216 {
217     return m_networkInfo.callMethod<jboolean>("isAvailable");
218 }
219 
isConnected() const220 bool AndroidNetworkInfo::isConnected() const
221 {
222     return m_networkInfo.callMethod<jboolean>("isConnected");
223 }
224 
isConnectedOrConnecting() const225 bool AndroidNetworkInfo::isConnectedOrConnecting() const
226 {
227     return m_networkInfo.callMethod<jboolean>("isConnectedOrConnecting");
228 }
229 
isFailover() const230 bool AndroidNetworkInfo::isFailover() const
231 {
232     return m_networkInfo.callMethod<jboolean>("isFailover");
233 }
234 
isRoaming() const235 bool AndroidNetworkInfo::isRoaming() const
236 {
237     return m_networkInfo.callMethod<jboolean>("isRoaming");
238 }
239 
isValid() const240 bool AndroidNetworkInfo::isValid() const
241 {
242     return m_networkInfo.isValid();
243 }
244 
AndroidConnectivityManager()245 AndroidConnectivityManager::AndroidConnectivityManager()
246 {
247     QJNIEnvironmentPrivate env;
248     if (!registerNatives(env))
249         return;
250 
251     m_connectivityManager = QJNIObjectPrivate::callStaticObjectMethod(networkReceiverClass,
252                                                                       "getConnectivityManager",
253                                                                       "(Landroid/content/Context;)Landroid/net/ConnectivityManager;",
254                                                                       QtAndroidPrivate::context());
255     if (!m_connectivityManager.isValid())
256         return;
257 
258     QJNIObjectPrivate::callStaticMethod<void>(networkReceiverClass,
259                                               "registerReceiver",
260                                               "(Landroid/content/Context;)V",
261                                               QtAndroidPrivate::context());
262 }
263 
getInstance()264 AndroidConnectivityManager *AndroidConnectivityManager::getInstance()
265 {
266     return androidConnManagerInstance->connManager->isValid()
267             ? androidConnManagerInstance->connManager
268             : 0;
269 }
270 
~AndroidConnectivityManager()271 AndroidConnectivityManager::~AndroidConnectivityManager()
272 {
273     QJNIObjectPrivate::callStaticMethod<void>(networkReceiverClass,
274                                               "unregisterReceiver",
275                                               "(Landroid/content/Context;)V",
276                                               QtAndroidPrivate::context());
277 }
278 
getActiveNetworkInfo() const279 AndroidNetworkInfo AndroidConnectivityManager::getActiveNetworkInfo() const
280 {
281     QJNIObjectPrivate networkInfo = m_connectivityManager.callObjectMethod("getActiveNetworkInfo",
282                                                                            "()Landroid/net/NetworkInfo;");
283     return networkInfo;
284 }
285 
getAllNetworkInfo() const286 QList<AndroidNetworkInfo> AndroidConnectivityManager::getAllNetworkInfo() const
287 {
288     QJNIEnvironmentPrivate env;
289     QJNIObjectPrivate objArray = m_connectivityManager.callObjectMethod("getAllNetworkInfo",
290                                                                         "()[Landroid/net/NetworkInfo;");
291     QList<AndroidNetworkInfo> list;
292     if (!objArray.isValid())
293         return list;
294 
295     const jsize length = env->GetArrayLength(static_cast<jarray>(objArray.object()));
296     if (exceptionCheckAndClear(env))
297         return list;
298 
299     for (int i = 0; i != length; ++i) {
300         jobject lref = env->GetObjectArrayElement(static_cast<jobjectArray>(objArray.object()), i);
301         if (exceptionCheckAndClear(env))
302             break;
303 
304         list << AndroidNetworkInfo(QJNIObjectPrivate::fromLocalRef(lref));
305     }
306 
307     return list;
308 }
309 
getBackgroundDataSetting() const310 bool AndroidConnectivityManager::getBackgroundDataSetting() const
311 {
312     return m_connectivityManager.callMethod<jboolean>("getBackgroundDataSetting");
313 }
314 
getNetworkInfo(int networkType) const315 AndroidNetworkInfo AndroidConnectivityManager::getNetworkInfo(int networkType) const
316 {
317     QJNIObjectPrivate networkInfo = m_connectivityManager.callObjectMethod("getNetworkInfo",
318                                                                            "(I)Landroid/net/NetworkInfo;",
319                                                                            networkType);
320     return networkInfo;
321 }
322 
getNetworkPreference() const323 int AndroidConnectivityManager::getNetworkPreference() const
324 {
325     return m_connectivityManager.callMethod<jint>("getNetworkPreference");
326 }
327 
isActiveNetworkMetered() const328 bool AndroidConnectivityManager::isActiveNetworkMetered() const
329 {
330     return m_connectivityManager.callMethod<jboolean>("isActiveNetworkMetered");
331 }
332 
isNetworkTypeValid(int networkType)333 bool AndroidConnectivityManager::isNetworkTypeValid(int networkType)
334 {
335     return QJNIObjectPrivate::callStaticMethod<jboolean>("android/net/ConnectivityManager",
336                                                          "isNetworkTypeValid",
337                                                          "(I)Z",
338                                                          networkType);
339 }
340 
requestRouteToHost(int networkType,int hostAddress)341 bool AndroidConnectivityManager::requestRouteToHost(int networkType, int hostAddress)
342 {
343     return m_connectivityManager.callMethod<jboolean>("requestRouteToHost", "(II)Z", networkType, hostAddress);
344 }
345 
setNetworkPreference(int preference)346 void AndroidConnectivityManager::setNetworkPreference(int preference)
347 {
348     m_connectivityManager.callMethod<void>("setNetworkPreference", "(I)V", preference);
349 }
350 
startUsingNetworkFeature(int networkType,const QString & feature)351 int AndroidConnectivityManager::startUsingNetworkFeature(int networkType, const QString &feature)
352 {
353     QJNIObjectPrivate jfeature = QJNIObjectPrivate::fromString(feature);
354     return m_connectivityManager.callMethod<jint>("startUsingNetworkFeature",
355                                                   "(ILjava/lang/String;)I",
356                                                   networkType,
357                                                   jfeature.object());
358 }
359 
stopUsingNetworkFeature(int networkType,const QString & feature)360 int AndroidConnectivityManager::stopUsingNetworkFeature(int networkType, const QString &feature)
361 {
362     QJNIObjectPrivate jfeature = QJNIObjectPrivate::fromString(feature);
363     return m_connectivityManager.callMethod<jint>("stopUsingNetworkFeature",
364                                                   "(ILjava/lang/String;)I",
365                                                   networkType,
366                                                   jfeature.object());
367 }
368 
activeNetworkInfoChanged()369 static void activeNetworkInfoChanged()
370 {
371     Q_EMIT androidConnManagerInstance->connManager->activeNetworkChanged();
372 }
373 
registerNatives(JNIEnv * env)374 bool AndroidConnectivityManager::registerNatives(JNIEnv *env)
375 {
376     QJNIObjectPrivate networkReceiver(networkReceiverClass);
377     if (!networkReceiver.isValid())
378         return false;
379 
380     jclass clazz = env->GetObjectClass(networkReceiver.object());
381     static JNINativeMethod method = {"activeNetworkInfoChanged", "()V", reinterpret_cast<void *>(activeNetworkInfoChanged)};
382     const bool ret = (env->RegisterNatives(clazz, &method, 1) == JNI_OK);
383     env->DeleteLocalRef(clazz);
384     return ret;
385 }
386 
387 QT_END_NAMESPACE
388