OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/api/android/jni/androidnetworkmonitor_jni.h" | |
12 | |
13 #include <dlfcn.h> | |
14 // This was added in Lollipop to dlfcn.h | |
15 #define RTLD_NOLOAD 4 | |
16 | |
17 #include "webrtc/api/android/jni/classreferenceholder.h" | |
18 #include "webrtc/api/android/jni/jni_helpers.h" | |
19 #include "webrtc/base/bind.h" | |
20 #include "webrtc/base/common.h" | |
21 #include "webrtc/base/ipaddress.h" | |
22 | |
23 namespace webrtc_jni { | |
24 | |
25 enum AndroidSdkVersion { | |
26 SDK_VERSION_LOLLIPOP = 21, | |
27 SDK_VERSION_MARSHMALLOW = 23 | |
28 }; | |
29 | |
30 jobject AndroidNetworkMonitor::application_context_ = nullptr; | |
31 int AndroidNetworkMonitor::android_sdk_int_ = 0; | |
32 | |
33 static NetworkType GetNetworkTypeFromJava(JNIEnv* jni, jobject j_network_type) { | |
34 std::string enum_name = | |
35 GetJavaEnumName(jni, "org/webrtc/NetworkMonitorAutoDetect$ConnectionType", | |
36 j_network_type); | |
37 if (enum_name == "CONNECTION_UNKNOWN") { | |
38 return NetworkType::NETWORK_UNKNOWN; | |
39 } | |
40 if (enum_name == "CONNECTION_ETHERNET") { | |
41 return NetworkType::NETWORK_ETHERNET; | |
42 } | |
43 if (enum_name == "CONNECTION_WIFI") { | |
44 return NetworkType::NETWORK_WIFI; | |
45 } | |
46 if (enum_name == "CONNECTION_4G") { | |
47 return NetworkType::NETWORK_4G; | |
48 } | |
49 if (enum_name == "CONNECTION_3G") { | |
50 return NetworkType::NETWORK_3G; | |
51 } | |
52 if (enum_name == "CONNECTION_2G") { | |
53 return NetworkType::NETWORK_2G; | |
54 } | |
55 if (enum_name == "CONNECTION_UNKNOWN_CELLULAR") { | |
56 return NetworkType::NETWORK_UNKNOWN_CELLULAR; | |
57 } | |
58 if (enum_name == "CONNECTION_BLUETOOTH") { | |
59 return NetworkType::NETWORK_BLUETOOTH; | |
60 } | |
61 if (enum_name == "CONNECTION_NONE") { | |
62 return NetworkType::NETWORK_NONE; | |
63 } | |
64 ASSERT(false); | |
65 return NetworkType::NETWORK_UNKNOWN; | |
66 } | |
67 | |
68 static rtc::AdapterType AdapterTypeFromNetworkType(NetworkType network_type) { | |
69 switch (network_type) { | |
70 case NETWORK_UNKNOWN: | |
71 return rtc::ADAPTER_TYPE_UNKNOWN; | |
72 case NETWORK_ETHERNET: | |
73 return rtc::ADAPTER_TYPE_ETHERNET; | |
74 case NETWORK_WIFI: | |
75 return rtc::ADAPTER_TYPE_WIFI; | |
76 case NETWORK_4G: | |
77 case NETWORK_3G: | |
78 case NETWORK_2G: | |
79 case NETWORK_UNKNOWN_CELLULAR: | |
80 return rtc::ADAPTER_TYPE_CELLULAR; | |
81 case NETWORK_BLUETOOTH: | |
82 // There is no corresponding mapping for bluetooth networks. | |
83 // Map it to VPN for now. | |
84 return rtc::ADAPTER_TYPE_VPN; | |
85 default: | |
86 RTC_DCHECK(false) << "Invalid network type " << network_type; | |
87 return rtc::ADAPTER_TYPE_UNKNOWN; | |
88 } | |
89 } | |
90 | |
91 static rtc::IPAddress GetIPAddressFromJava(JNIEnv* jni, jobject j_ip_address) { | |
92 jclass j_ip_address_class = GetObjectClass(jni, j_ip_address); | |
93 jfieldID j_address_id = GetFieldID(jni, j_ip_address_class, "address", "[B"); | |
94 jbyteArray j_addresses = | |
95 static_cast<jbyteArray>(GetObjectField(jni, j_ip_address, j_address_id)); | |
96 size_t address_length = jni->GetArrayLength(j_addresses); | |
97 jbyte* addr_array = jni->GetByteArrayElements(j_addresses, nullptr); | |
98 CHECK_EXCEPTION(jni) << "Error during GetIPAddressFromJava"; | |
99 if (address_length == 4) { | |
100 // IP4 | |
101 struct in_addr ip4_addr; | |
102 memcpy(&ip4_addr.s_addr, addr_array, 4); | |
103 jni->ReleaseByteArrayElements(j_addresses, addr_array, JNI_ABORT); | |
104 return rtc::IPAddress(ip4_addr); | |
105 } | |
106 // IP6 | |
107 RTC_CHECK(address_length == 16); | |
108 struct in6_addr ip6_addr; | |
109 memcpy(ip6_addr.s6_addr, addr_array, address_length); | |
110 jni->ReleaseByteArrayElements(j_addresses, addr_array, JNI_ABORT); | |
111 return rtc::IPAddress(ip6_addr); | |
112 } | |
113 | |
114 static void GetIPAddressesFromJava(JNIEnv* jni, | |
115 jobjectArray j_ip_addresses, | |
116 std::vector<rtc::IPAddress>* ip_addresses) { | |
117 ip_addresses->clear(); | |
118 size_t num_addresses = jni->GetArrayLength(j_ip_addresses); | |
119 CHECK_EXCEPTION(jni) << "Error during GetArrayLength"; | |
120 for (size_t i = 0; i < num_addresses; ++i) { | |
121 jobject j_ip_address = jni->GetObjectArrayElement(j_ip_addresses, i); | |
122 CHECK_EXCEPTION(jni) << "Error during GetObjectArrayElement"; | |
123 rtc::IPAddress ip = GetIPAddressFromJava(jni, j_ip_address); | |
124 ip_addresses->push_back(ip); | |
125 } | |
126 } | |
127 | |
128 static NetworkInformation GetNetworkInformationFromJava( | |
129 JNIEnv* jni, | |
130 jobject j_network_info) { | |
131 jclass j_network_info_class = GetObjectClass(jni, j_network_info); | |
132 jfieldID j_interface_name_id = | |
133 GetFieldID(jni, j_network_info_class, "name", "Ljava/lang/String;"); | |
134 jfieldID j_handle_id = GetFieldID(jni, j_network_info_class, "handle", "J"); | |
135 jfieldID j_type_id = | |
136 GetFieldID(jni, j_network_info_class, "type", | |
137 "Lorg/webrtc/NetworkMonitorAutoDetect$ConnectionType;"); | |
138 jfieldID j_ip_addresses_id = | |
139 GetFieldID(jni, j_network_info_class, "ipAddresses", | |
140 "[Lorg/webrtc/NetworkMonitorAutoDetect$IPAddress;"); | |
141 | |
142 NetworkInformation network_info; | |
143 network_info.interface_name = JavaToStdString( | |
144 jni, GetStringField(jni, j_network_info, j_interface_name_id)); | |
145 network_info.handle = static_cast<NetworkHandle>( | |
146 GetLongField(jni, j_network_info, j_handle_id)); | |
147 network_info.type = GetNetworkTypeFromJava( | |
148 jni, GetObjectField(jni, j_network_info, j_type_id)); | |
149 jobjectArray j_ip_addresses = static_cast<jobjectArray>( | |
150 GetObjectField(jni, j_network_info, j_ip_addresses_id)); | |
151 GetIPAddressesFromJava(jni, j_ip_addresses, &network_info.ip_addresses); | |
152 return network_info; | |
153 } | |
154 | |
155 std::string NetworkInformation::ToString() const { | |
156 std::stringstream ss; | |
157 ss << "NetInfo[name " << interface_name << "; handle " << handle << "; type " | |
158 << type << "; address"; | |
159 for (const rtc::IPAddress address : ip_addresses) { | |
160 ss << " " << address.ToString(); | |
161 } | |
162 ss << "]"; | |
163 return ss.str(); | |
164 } | |
165 | |
166 // static | |
167 void AndroidNetworkMonitor::SetAndroidContext(JNIEnv* jni, jobject context) { | |
168 if (application_context_) { | |
169 jni->DeleteGlobalRef(application_context_); | |
170 } | |
171 application_context_ = NewGlobalRef(jni, context); | |
172 } | |
173 | |
174 AndroidNetworkMonitor::AndroidNetworkMonitor() | |
175 : j_network_monitor_class_(jni(), | |
176 FindClass(jni(), "org/webrtc/NetworkMonitor")), | |
177 j_network_monitor_( | |
178 jni(), | |
179 jni()->CallStaticObjectMethod( | |
180 *j_network_monitor_class_, | |
181 GetStaticMethodID( | |
182 jni(), | |
183 *j_network_monitor_class_, | |
184 "init", | |
185 "(Landroid/content/Context;)Lorg/webrtc/NetworkMonitor;"), | |
186 application_context_)) { | |
187 ASSERT(application_context_ != nullptr); | |
188 CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.init"; | |
189 if (android_sdk_int_ <= 0) { | |
190 jmethodID m = GetStaticMethodID(jni(), *j_network_monitor_class_, | |
191 "androidSdkInt", "()I"); | |
192 android_sdk_int_ = jni()->CallStaticIntMethod(*j_network_monitor_class_, m); | |
193 CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.androidSdkInt"; | |
194 } | |
195 } | |
196 | |
197 void AndroidNetworkMonitor::Start() { | |
198 RTC_CHECK(thread_checker_.CalledOnValidThread()); | |
199 if (started_) { | |
200 return; | |
201 } | |
202 started_ = true; | |
203 | |
204 // This is kind of magic behavior, but doing this allows the SocketServer to | |
205 // use this as a NetworkBinder to bind sockets on a particular network when | |
206 // it creates sockets. | |
207 worker_thread()->socketserver()->set_network_binder(this); | |
208 | |
209 jmethodID m = | |
210 GetMethodID(jni(), *j_network_monitor_class_, "startMonitoring", "(J)V"); | |
211 jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this)); | |
212 CHECK_EXCEPTION(jni()) << "Error during CallVoidMethod"; | |
213 } | |
214 | |
215 void AndroidNetworkMonitor::Stop() { | |
216 RTC_CHECK(thread_checker_.CalledOnValidThread()); | |
217 if (!started_) { | |
218 return; | |
219 } | |
220 started_ = false; | |
221 | |
222 // Once the network monitor stops, it will clear all network information and | |
223 // it won't find the network handle to bind anyway. | |
224 if (worker_thread()->socketserver()->network_binder() == this) { | |
225 worker_thread()->socketserver()->set_network_binder(nullptr); | |
226 } | |
227 | |
228 jmethodID m = | |
229 GetMethodID(jni(), *j_network_monitor_class_, "stopMonitoring", "(J)V"); | |
230 jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this)); | |
231 CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.stopMonitoring"; | |
232 | |
233 network_handle_by_address_.clear(); | |
234 network_info_by_handle_.clear(); | |
235 } | |
236 | |
237 // The implementation is largely taken from UDPSocketPosix::BindToNetwork in | |
238 // https://cs.chromium.org/chromium/src/net/udp/udp_socket_posix.cc | |
239 int AndroidNetworkMonitor::BindSocketToNetwork(int socket_fd, | |
240 const rtc::IPAddress& address) { | |
241 RTC_CHECK(thread_checker_.CalledOnValidThread()); | |
242 // Android prior to Lollipop didn't have support for binding sockets to | |
243 // networks. In that case it should not have reached here because | |
244 // |network_handle_by_address_| is only populated in Android Lollipop | |
245 // and above. | |
246 if (android_sdk_int_ < SDK_VERSION_LOLLIPOP) { | |
247 LOG(LS_ERROR) << "BindSocketToNetwork is not supported in Android SDK " | |
248 << android_sdk_int_; | |
249 return rtc::NETWORK_BIND_NOT_IMPLEMENTED; | |
250 } | |
251 | |
252 auto iter = network_handle_by_address_.find(address); | |
253 if (iter == network_handle_by_address_.end()) { | |
254 return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND; | |
255 } | |
256 NetworkHandle network_handle = iter->second; | |
257 | |
258 int rv = 0; | |
259 if (android_sdk_int_ >= SDK_VERSION_MARSHMALLOW) { | |
260 // See declaration of android_setsocknetwork() here: | |
261 // http://androidxref.com/6.0.0_r1/xref/development/ndk/platforms/android-M/
include/android/multinetwork.h#65 | |
262 // Function cannot be called directly as it will cause app to fail to load | |
263 // on pre-marshmallow devices. | |
264 typedef int (*MarshmallowSetNetworkForSocket)(NetworkHandle net, | |
265 int socket); | |
266 static MarshmallowSetNetworkForSocket marshmallowSetNetworkForSocket; | |
267 // This is not thread-safe, but we are running this only on the worker | |
268 // thread. | |
269 if (!marshmallowSetNetworkForSocket) { | |
270 const std::string android_native_lib_path = "libandroid.so"; | |
271 void* lib = dlopen(android_native_lib_path.c_str(), RTLD_NOW); | |
272 if (lib == nullptr) { | |
273 LOG(LS_ERROR) << "Library " << android_native_lib_path << " not found!"; | |
274 return rtc::NETWORK_BIND_NOT_IMPLEMENTED; | |
275 } | |
276 marshmallowSetNetworkForSocket = | |
277 reinterpret_cast<MarshmallowSetNetworkForSocket>( | |
278 dlsym(lib, "android_setsocknetwork")); | |
279 } | |
280 if (!marshmallowSetNetworkForSocket) { | |
281 LOG(LS_ERROR) << "Symbol marshmallowSetNetworkForSocket is not found"; | |
282 return rtc::NETWORK_BIND_NOT_IMPLEMENTED; | |
283 } | |
284 rv = marshmallowSetNetworkForSocket(network_handle, socket_fd); | |
285 } else { | |
286 // NOTE: This relies on Android implementation details, but it won't change | |
287 // because Lollipop is already released. | |
288 typedef int (*LollipopSetNetworkForSocket)(unsigned net, int socket); | |
289 static LollipopSetNetworkForSocket lollipopSetNetworkForSocket; | |
290 // This is not threadsafe, but we are running this only on the worker | |
291 // thread. | |
292 if (!lollipopSetNetworkForSocket) { | |
293 // Android's netd client library should always be loaded in our address | |
294 // space as it shims libc functions like connect(). | |
295 const std::string net_library_path = "libnetd_client.so"; | |
296 // Use RTLD_NOW to match Android's prior loading of the library: | |
297 // http://androidxref.com/6.0.0_r5/xref/bionic/libc/bionic/NetdClient.cpp#
37 | |
298 // Use RTLD_NOLOAD to assert that the library is already loaded and | |
299 // avoid doing any disk IO. | |
300 void* lib = dlopen(net_library_path.c_str(), RTLD_NOW | RTLD_NOLOAD); | |
301 if (lib == nullptr) { | |
302 LOG(LS_ERROR) << "Library " << net_library_path << " not found!"; | |
303 return rtc::NETWORK_BIND_NOT_IMPLEMENTED; | |
304 } | |
305 lollipopSetNetworkForSocket = | |
306 reinterpret_cast<LollipopSetNetworkForSocket>( | |
307 dlsym(lib, "setNetworkForSocket")); | |
308 } | |
309 if (!lollipopSetNetworkForSocket) { | |
310 LOG(LS_ERROR) << "Symbol lollipopSetNetworkForSocket is not found "; | |
311 return rtc::NETWORK_BIND_NOT_IMPLEMENTED; | |
312 } | |
313 rv = lollipopSetNetworkForSocket(network_handle, socket_fd); | |
314 } | |
315 | |
316 // If |network| has since disconnected, |rv| will be ENONET. Surface this as | |
317 // ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back | |
318 // the less descriptive ERR_FAILED. | |
319 if (rv == 0) { | |
320 return rtc::NETWORK_BIND_SUCCESS; | |
321 } | |
322 if (rv == ENONET) { | |
323 return rtc::NETWORK_BIND_NETWORK_CHANGED; | |
324 } | |
325 return rtc::NETWORK_BIND_FAILURE; | |
326 } | |
327 | |
328 void AndroidNetworkMonitor::OnNetworkConnected( | |
329 const NetworkInformation& network_info) { | |
330 worker_thread()->Invoke<void>( | |
331 RTC_FROM_HERE, rtc::Bind(&AndroidNetworkMonitor::OnNetworkConnected_w, | |
332 this, network_info)); | |
333 // Fire SignalNetworksChanged to update the list of networks. | |
334 OnNetworksChanged(); | |
335 } | |
336 | |
337 void AndroidNetworkMonitor::OnNetworkConnected_w( | |
338 const NetworkInformation& network_info) { | |
339 LOG(LS_INFO) << "Network connected: " << network_info.ToString(); | |
340 adapter_type_by_name_[network_info.interface_name] = | |
341 AdapterTypeFromNetworkType(network_info.type); | |
342 network_info_by_handle_[network_info.handle] = network_info; | |
343 for (const rtc::IPAddress& address : network_info.ip_addresses) { | |
344 network_handle_by_address_[address] = network_info.handle; | |
345 } | |
346 } | |
347 | |
348 void AndroidNetworkMonitor::OnNetworkDisconnected(NetworkHandle handle) { | |
349 LOG(LS_INFO) << "Network disconnected for handle " << handle; | |
350 worker_thread()->Invoke<void>( | |
351 RTC_FROM_HERE, | |
352 rtc::Bind(&AndroidNetworkMonitor::OnNetworkDisconnected_w, this, handle)); | |
353 } | |
354 | |
355 void AndroidNetworkMonitor::OnNetworkDisconnected_w(NetworkHandle handle) { | |
356 auto iter = network_info_by_handle_.find(handle); | |
357 if (iter != network_info_by_handle_.end()) { | |
358 for (const rtc::IPAddress& address : iter->second.ip_addresses) { | |
359 network_handle_by_address_.erase(address); | |
360 } | |
361 network_info_by_handle_.erase(iter); | |
362 } | |
363 } | |
364 | |
365 void AndroidNetworkMonitor::SetNetworkInfos( | |
366 const std::vector<NetworkInformation>& network_infos) { | |
367 RTC_CHECK(thread_checker_.CalledOnValidThread()); | |
368 network_handle_by_address_.clear(); | |
369 network_info_by_handle_.clear(); | |
370 LOG(LS_INFO) << "Android network monitor found " << network_infos.size() | |
371 << " networks"; | |
372 for (NetworkInformation network : network_infos) { | |
373 OnNetworkConnected_w(network); | |
374 } | |
375 } | |
376 | |
377 rtc::AdapterType AndroidNetworkMonitor::GetAdapterType( | |
378 const std::string& if_name) { | |
379 auto iter = adapter_type_by_name_.find(if_name); | |
380 rtc::AdapterType type = (iter == adapter_type_by_name_.end()) | |
381 ? rtc::ADAPTER_TYPE_UNKNOWN | |
382 : iter->second; | |
383 if (type == rtc::ADAPTER_TYPE_UNKNOWN) { | |
384 LOG(LS_WARNING) << "Get an unknown type for the interface " << if_name; | |
385 } | |
386 return type; | |
387 } | |
388 | |
389 rtc::NetworkMonitorInterface* | |
390 AndroidNetworkMonitorFactory::CreateNetworkMonitor() { | |
391 return new AndroidNetworkMonitor(); | |
392 } | |
393 | |
394 JOW(void, NetworkMonitor_nativeNotifyConnectionTypeChanged)( | |
395 JNIEnv* jni, jobject j_monitor, jlong j_native_monitor) { | |
396 rtc::NetworkMonitorInterface* network_monitor = | |
397 reinterpret_cast<rtc::NetworkMonitorInterface*>(j_native_monitor); | |
398 network_monitor->OnNetworksChanged(); | |
399 } | |
400 | |
401 JOW(void, NetworkMonitor_nativeNotifyOfActiveNetworkList)( | |
402 JNIEnv* jni, jobject j_monitor, jlong j_native_monitor, | |
403 jobjectArray j_network_infos) { | |
404 AndroidNetworkMonitor* network_monitor = | |
405 reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor); | |
406 std::vector<NetworkInformation> network_infos; | |
407 size_t num_networks = jni->GetArrayLength(j_network_infos); | |
408 for (size_t i = 0; i < num_networks; ++i) { | |
409 jobject j_network_info = jni->GetObjectArrayElement(j_network_infos, i); | |
410 CHECK_EXCEPTION(jni) << "Error during GetObjectArrayElement"; | |
411 network_infos.push_back(GetNetworkInformationFromJava(jni, j_network_info)); | |
412 } | |
413 network_monitor->SetNetworkInfos(network_infos); | |
414 } | |
415 | |
416 JOW(void, NetworkMonitor_nativeNotifyOfNetworkConnect)( | |
417 JNIEnv* jni, jobject j_monitor, jlong j_native_monitor, | |
418 jobject j_network_info) { | |
419 AndroidNetworkMonitor* network_monitor = | |
420 reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor); | |
421 NetworkInformation network_info = | |
422 GetNetworkInformationFromJava(jni, j_network_info); | |
423 network_monitor->OnNetworkConnected(network_info); | |
424 } | |
425 | |
426 JOW(void, NetworkMonitor_nativeNotifyOfNetworkDisconnect)( | |
427 JNIEnv* jni, jobject j_monitor, jlong j_native_monitor, | |
428 jlong network_handle) { | |
429 AndroidNetworkMonitor* network_monitor = | |
430 reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor); | |
431 network_monitor->OnNetworkDisconnected( | |
432 static_cast<NetworkHandle>(network_handle)); | |
433 } | |
434 | |
435 } // namespace webrtc_jni | |
OLD | NEW |