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