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 "sdk/android/src/jni/android_network_monitor.h"
12
13 #include <dlfcn.h>
14 #ifndef RTLD_NOLOAD
15 // This was added in Lollipop to dlfcn.h
16 #define RTLD_NOLOAD 4
17 #endif
18
19 #include "rtc_base/bind.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/ip_address.h"
22 #include "rtc_base/logging.h"
23 #include "rtc_base/strings/string_builder.h"
24 #include "sdk/android/generated_base_jni/NetworkMonitorAutoDetect_jni.h"
25 #include "sdk/android/generated_base_jni/NetworkMonitor_jni.h"
26 #include "sdk/android/native_api/jni/java_types.h"
27 #include "sdk/android/src/jni/jni_helpers.h"
28 #include "system_wrappers/include/field_trial.h"
29
30 namespace webrtc {
31 namespace jni {
32
33 enum AndroidSdkVersion {
34 SDK_VERSION_LOLLIPOP = 21,
35 SDK_VERSION_MARSHMALLOW = 23
36 };
37
GetNetworkTypeFromJava(JNIEnv * jni,const JavaRef<jobject> & j_network_type)38 static NetworkType GetNetworkTypeFromJava(
39 JNIEnv* jni,
40 const JavaRef<jobject>& j_network_type) {
41 std::string enum_name = GetJavaEnumName(jni, j_network_type);
42 if (enum_name == "CONNECTION_UNKNOWN") {
43 return NetworkType::NETWORK_UNKNOWN;
44 }
45 if (enum_name == "CONNECTION_ETHERNET") {
46 return NetworkType::NETWORK_ETHERNET;
47 }
48 if (enum_name == "CONNECTION_WIFI") {
49 return NetworkType::NETWORK_WIFI;
50 }
51 if (enum_name == "CONNECTION_4G") {
52 return NetworkType::NETWORK_4G;
53 }
54 if (enum_name == "CONNECTION_3G") {
55 return NetworkType::NETWORK_3G;
56 }
57 if (enum_name == "CONNECTION_2G") {
58 return NetworkType::NETWORK_2G;
59 }
60 if (enum_name == "CONNECTION_UNKNOWN_CELLULAR") {
61 return NetworkType::NETWORK_UNKNOWN_CELLULAR;
62 }
63 if (enum_name == "CONNECTION_BLUETOOTH") {
64 return NetworkType::NETWORK_BLUETOOTH;
65 }
66 if (enum_name == "CONNECTION_VPN") {
67 return NetworkType::NETWORK_VPN;
68 }
69 if (enum_name == "CONNECTION_NONE") {
70 return NetworkType::NETWORK_NONE;
71 }
72 RTC_NOTREACHED();
73 return NetworkType::NETWORK_UNKNOWN;
74 }
75
AdapterTypeFromNetworkType(NetworkType network_type)76 static rtc::AdapterType AdapterTypeFromNetworkType(NetworkType network_type) {
77 switch (network_type) {
78 case NETWORK_UNKNOWN:
79 return rtc::ADAPTER_TYPE_UNKNOWN;
80 case NETWORK_ETHERNET:
81 return rtc::ADAPTER_TYPE_ETHERNET;
82 case NETWORK_WIFI:
83 return rtc::ADAPTER_TYPE_WIFI;
84 case NETWORK_4G:
85 case NETWORK_3G:
86 case NETWORK_2G:
87 case NETWORK_UNKNOWN_CELLULAR:
88 return rtc::ADAPTER_TYPE_CELLULAR;
89 case NETWORK_VPN:
90 case NETWORK_BLUETOOTH:
91 // There is no corresponding mapping for bluetooth networks.
92 // Map it to VPN for now.
93 return rtc::ADAPTER_TYPE_VPN;
94 default:
95 RTC_NOTREACHED() << "Invalid network type " << network_type;
96 return rtc::ADAPTER_TYPE_UNKNOWN;
97 }
98 }
99
JavaToNativeIpAddress(JNIEnv * jni,const JavaRef<jobject> & j_ip_address)100 static rtc::IPAddress JavaToNativeIpAddress(
101 JNIEnv* jni,
102 const JavaRef<jobject>& j_ip_address) {
103 std::vector<int8_t> address =
104 JavaToNativeByteArray(jni, Java_IPAddress_getAddress(jni, j_ip_address));
105 size_t address_length = address.size();
106 if (address_length == 4) {
107 // IP4
108 struct in_addr ip4_addr;
109 memcpy(&ip4_addr.s_addr, address.data(), 4);
110 return rtc::IPAddress(ip4_addr);
111 }
112 // IP6
113 RTC_CHECK(address_length == 16);
114 struct in6_addr ip6_addr;
115 memcpy(ip6_addr.s6_addr, address.data(), address_length);
116 return rtc::IPAddress(ip6_addr);
117 }
118
GetNetworkInformationFromJava(JNIEnv * jni,const JavaRef<jobject> & j_network_info)119 static NetworkInformation GetNetworkInformationFromJava(
120 JNIEnv* jni,
121 const JavaRef<jobject>& j_network_info) {
122 NetworkInformation network_info;
123 network_info.interface_name = JavaToStdString(
124 jni, Java_NetworkInformation_getName(jni, j_network_info));
125 network_info.handle = static_cast<NetworkHandle>(
126 Java_NetworkInformation_getHandle(jni, j_network_info));
127 network_info.type = GetNetworkTypeFromJava(
128 jni, Java_NetworkInformation_getConnectionType(jni, j_network_info));
129 network_info.underlying_type_for_vpn = GetNetworkTypeFromJava(
130 jni, Java_NetworkInformation_getUnderlyingConnectionTypeForVpn(
131 jni, j_network_info));
132 ScopedJavaLocalRef<jobjectArray> j_ip_addresses =
133 Java_NetworkInformation_getIpAddresses(jni, j_network_info);
134 network_info.ip_addresses = JavaToNativeVector<rtc::IPAddress>(
135 jni, j_ip_addresses, &JavaToNativeIpAddress);
136 return network_info;
137 }
138
AddressMatch(const rtc::IPAddress & ip1,const rtc::IPAddress & ip2)139 static bool AddressMatch(const rtc::IPAddress& ip1, const rtc::IPAddress& ip2) {
140 if (ip1.family() != ip2.family()) {
141 return false;
142 }
143 if (ip1.family() == AF_INET) {
144 return ip1.ipv4_address().s_addr == ip2.ipv4_address().s_addr;
145 }
146 if (ip1.family() == AF_INET6) {
147 // The last 64-bits of an ipv6 address are temporary address and it could
148 // change over time. So we only compare the first 64-bits.
149 return memcmp(ip1.ipv6_address().s6_addr, ip2.ipv6_address().s6_addr,
150 sizeof(in6_addr) / 2) == 0;
151 }
152 return false;
153 }
154
155 NetworkInformation::NetworkInformation() = default;
156
157 NetworkInformation::NetworkInformation(const NetworkInformation&) = default;
158
159 NetworkInformation::NetworkInformation(NetworkInformation&&) = default;
160
161 NetworkInformation::~NetworkInformation() = default;
162
163 NetworkInformation& NetworkInformation::operator=(const NetworkInformation&) =
164 default;
165
166 NetworkInformation& NetworkInformation::operator=(NetworkInformation&&) =
167 default;
168
ToString() const169 std::string NetworkInformation::ToString() const {
170 rtc::StringBuilder ss;
171 ss << "NetInfo[name " << interface_name << "; handle " << handle << "; type "
172 << type;
173 if (type == NETWORK_VPN) {
174 ss << "; underlying_type_for_vpn " << underlying_type_for_vpn;
175 }
176 ss << "]";
177 return ss.Release();
178 }
179
AndroidNetworkMonitor(JNIEnv * env,const JavaRef<jobject> & j_application_context)180 AndroidNetworkMonitor::AndroidNetworkMonitor(
181 JNIEnv* env,
182 const JavaRef<jobject>& j_application_context)
183 : android_sdk_int_(Java_NetworkMonitor_androidSdkInt(env)),
184 j_application_context_(env, j_application_context),
185 j_network_monitor_(env, Java_NetworkMonitor_getInstance(env)) {}
186
187 AndroidNetworkMonitor::~AndroidNetworkMonitor() = default;
188
Start()189 void AndroidNetworkMonitor::Start() {
190 RTC_CHECK(thread_checker_.IsCurrent());
191 if (started_) {
192 return;
193 }
194 started_ = true;
195 find_network_handle_without_ipv6_temporary_part_ =
196 webrtc::field_trial::IsEnabled(
197 "WebRTC-FindNetworkHandleWithoutIpv6TemporaryPart");
198
199 // This is kind of magic behavior, but doing this allows the SocketServer to
200 // use this as a NetworkBinder to bind sockets on a particular network when
201 // it creates sockets.
202 worker_thread()->socketserver()->set_network_binder(this);
203
204 JNIEnv* env = AttachCurrentThreadIfNeeded();
205 Java_NetworkMonitor_startMonitoring(
206 env, j_network_monitor_, j_application_context_, jlongFromPointer(this));
207 }
208
Stop()209 void AndroidNetworkMonitor::Stop() {
210 RTC_CHECK(thread_checker_.IsCurrent());
211 if (!started_) {
212 return;
213 }
214 started_ = false;
215 find_network_handle_without_ipv6_temporary_part_ = false;
216
217 // Once the network monitor stops, it will clear all network information and
218 // it won't find the network handle to bind anyway.
219 if (worker_thread()->socketserver()->network_binder() == this) {
220 worker_thread()->socketserver()->set_network_binder(nullptr);
221 }
222
223 JNIEnv* env = AttachCurrentThreadIfNeeded();
224 Java_NetworkMonitor_stopMonitoring(env, j_network_monitor_,
225 jlongFromPointer(this));
226
227 network_handle_by_address_.clear();
228 network_info_by_handle_.clear();
229 }
230
231 // The implementation is largely taken from UDPSocketPosix::BindToNetwork in
232 // https://cs.chromium.org/chromium/src/net/udp/udp_socket_posix.cc
BindSocketToNetwork(int socket_fd,const rtc::IPAddress & address)233 rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork(
234 int socket_fd,
235 const rtc::IPAddress& address) {
236 RTC_CHECK(thread_checker_.IsCurrent());
237
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
240 // service.
241 JNIEnv* env = AttachCurrentThreadIfNeeded();
242 const bool network_binding_supported =
243 Java_NetworkMonitor_networkBindingSupported(env, j_network_monitor_);
244 if (!network_binding_supported) {
245 RTC_LOG(LS_WARNING)
246 << "BindSocketToNetwork is not supported on this platform "
247 "(Android SDK: "
248 << android_sdk_int_ << ")";
249 return rtc::NetworkBindingResult::NOT_IMPLEMENTED;
250 }
251
252 absl::optional<NetworkHandle> network_handle =
253 FindNetworkHandleFromAddress(address);
254 if (!network_handle) {
255 return rtc::NetworkBindingResult::ADDRESS_NOT_FOUND;
256 }
257
258 if (*network_handle == 0 /* NETWORK_UNSPECIFIED */) {
259 return rtc::NetworkBindingResult::NOT_IMPLEMENTED;
260 }
261
262 int rv = 0;
263 if (android_sdk_int_ >= SDK_VERSION_MARSHMALLOW) {
264 // See declaration of android_setsocknetwork() here:
265 // http://androidxref.com/6.0.0_r1/xref/development/ndk/platforms/android-M/include/android/multinetwork.h#65
266 // Function cannot be called directly as it will cause app to fail to load
267 // on pre-marshmallow devices.
268 typedef int (*MarshmallowSetNetworkForSocket)(NetworkHandle net,
269 int socket);
270 static MarshmallowSetNetworkForSocket marshmallowSetNetworkForSocket;
271 // This is not thread-safe, but we are running this only on the worker
272 // thread.
273 if (!marshmallowSetNetworkForSocket) {
274 const std::string android_native_lib_path = "libandroid.so";
275 void* lib = dlopen(android_native_lib_path.c_str(), RTLD_NOW);
276 if (lib == nullptr) {
277 RTC_LOG(LS_ERROR) << "Library " << android_native_lib_path
278 << " not found!";
279 return rtc::NetworkBindingResult::NOT_IMPLEMENTED;
280 }
281 marshmallowSetNetworkForSocket =
282 reinterpret_cast<MarshmallowSetNetworkForSocket>(
283 dlsym(lib, "android_setsocknetwork"));
284 }
285 if (!marshmallowSetNetworkForSocket) {
286 RTC_LOG(LS_ERROR) << "Symbol marshmallowSetNetworkForSocket is not found";
287 return rtc::NetworkBindingResult::NOT_IMPLEMENTED;
288 }
289 rv = marshmallowSetNetworkForSocket(*network_handle, socket_fd);
290 } else {
291 // NOTE: This relies on Android implementation details, but it won't
292 // change because Lollipop is already released.
293 typedef int (*LollipopSetNetworkForSocket)(unsigned net, int socket);
294 static LollipopSetNetworkForSocket lollipopSetNetworkForSocket;
295 // This is not threadsafe, but we are running this only on the worker
296 // thread.
297 if (!lollipopSetNetworkForSocket) {
298 // Android's netd client library should always be loaded in our address
299 // space as it shims libc functions like connect().
300 const std::string net_library_path = "libnetd_client.so";
301 // Use RTLD_NOW to match Android's prior loading of the library:
302 // http://androidxref.com/6.0.0_r5/xref/bionic/libc/bionic/NetdClient.cpp#37
303 // Use RTLD_NOLOAD to assert that the library is already loaded and
304 // avoid doing any disk IO.
305 void* lib = dlopen(net_library_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
306 if (lib == nullptr) {
307 RTC_LOG(LS_ERROR) << "Library " << net_library_path << " not found!";
308 return rtc::NetworkBindingResult::NOT_IMPLEMENTED;
309 }
310 lollipopSetNetworkForSocket =
311 reinterpret_cast<LollipopSetNetworkForSocket>(
312 dlsym(lib, "setNetworkForSocket"));
313 }
314 if (!lollipopSetNetworkForSocket) {
315 RTC_LOG(LS_ERROR) << "Symbol lollipopSetNetworkForSocket is not found ";
316 return rtc::NetworkBindingResult::NOT_IMPLEMENTED;
317 }
318 rv = lollipopSetNetworkForSocket(*network_handle, socket_fd);
319 }
320
321 // If |network| has since disconnected, |rv| will be ENONET. Surface this as
322 // ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back
323 // the less descriptive ERR_FAILED.
324 if (rv == 0) {
325 return rtc::NetworkBindingResult::SUCCESS;
326 }
327 if (rv == ENONET) {
328 return rtc::NetworkBindingResult::NETWORK_CHANGED;
329 }
330 return rtc::NetworkBindingResult::FAILURE;
331 }
332
OnNetworkConnected(const NetworkInformation & network_info)333 void AndroidNetworkMonitor::OnNetworkConnected(
334 const NetworkInformation& network_info) {
335 worker_thread()->Invoke<void>(
336 RTC_FROM_HERE, rtc::Bind(&AndroidNetworkMonitor::OnNetworkConnected_w,
337 this, network_info));
338 // Fire SignalNetworksChanged to update the list of networks.
339 OnNetworksChanged();
340 }
341
OnNetworkConnected_w(const NetworkInformation & network_info)342 void AndroidNetworkMonitor::OnNetworkConnected_w(
343 const NetworkInformation& network_info) {
344 RTC_LOG(LS_INFO) << "Network connected: " << network_info.ToString();
345 adapter_type_by_name_[network_info.interface_name] =
346 AdapterTypeFromNetworkType(network_info.type);
347 if (network_info.type == NETWORK_VPN) {
348 vpn_underlying_adapter_type_by_name_[network_info.interface_name] =
349 AdapterTypeFromNetworkType(network_info.underlying_type_for_vpn);
350 }
351 network_info_by_handle_[network_info.handle] = network_info;
352 for (const rtc::IPAddress& address : network_info.ip_addresses) {
353 network_handle_by_address_[address] = network_info.handle;
354 }
355 }
356
357 absl::optional<NetworkHandle>
FindNetworkHandleFromAddress(const rtc::IPAddress & ip_address) const358 AndroidNetworkMonitor::FindNetworkHandleFromAddress(
359 const rtc::IPAddress& ip_address) const {
360 RTC_LOG(LS_INFO) << "Find network handle.";
361 if (find_network_handle_without_ipv6_temporary_part_) {
362 for (auto const& iter : network_info_by_handle_) {
363 const std::vector<rtc::IPAddress>& addresses = iter.second.ip_addresses;
364 auto address_it = std::find_if(addresses.begin(), addresses.end(),
365 [ip_address](rtc::IPAddress address) {
366 return AddressMatch(ip_address, address);
367 });
368 if (address_it != addresses.end()) {
369 return absl::make_optional(iter.first);
370 }
371 }
372 return absl::nullopt;
373 } else {
374 auto iter = network_handle_by_address_.find(ip_address);
375 if (iter == network_handle_by_address_.end()) {
376 return absl::nullopt;
377 }
378 return absl::make_optional(iter->second);
379 }
380 }
381
OnNetworkDisconnected(NetworkHandle handle)382 void AndroidNetworkMonitor::OnNetworkDisconnected(NetworkHandle handle) {
383 RTC_LOG(LS_INFO) << "Network disconnected for handle " << handle;
384 worker_thread()->Invoke<void>(
385 RTC_FROM_HERE,
386 rtc::Bind(&AndroidNetworkMonitor::OnNetworkDisconnected_w, this, handle));
387 }
388
OnNetworkDisconnected_w(NetworkHandle handle)389 void AndroidNetworkMonitor::OnNetworkDisconnected_w(NetworkHandle handle) {
390 auto iter = network_info_by_handle_.find(handle);
391 if (iter != network_info_by_handle_.end()) {
392 for (const rtc::IPAddress& address : iter->second.ip_addresses) {
393 network_handle_by_address_.erase(address);
394 }
395 network_info_by_handle_.erase(iter);
396 }
397 }
398
SetNetworkInfos(const std::vector<NetworkInformation> & network_infos)399 void AndroidNetworkMonitor::SetNetworkInfos(
400 const std::vector<NetworkInformation>& network_infos) {
401 RTC_CHECK(thread_checker_.IsCurrent());
402 network_handle_by_address_.clear();
403 network_info_by_handle_.clear();
404 RTC_LOG(LS_INFO) << "Android network monitor found " << network_infos.size()
405 << " networks";
406 for (NetworkInformation network : network_infos) {
407 OnNetworkConnected_w(network);
408 }
409 }
410
GetAdapterType(const std::string & if_name)411 rtc::AdapterType AndroidNetworkMonitor::GetAdapterType(
412 const std::string& if_name) {
413 auto iter = adapter_type_by_name_.find(if_name);
414 rtc::AdapterType type = (iter == adapter_type_by_name_.end())
415 ? rtc::ADAPTER_TYPE_UNKNOWN
416 : iter->second;
417 if (type == rtc::ADAPTER_TYPE_UNKNOWN) {
418 RTC_LOG(LS_WARNING) << "Get an unknown type for the interface " << if_name;
419 }
420 return type;
421 }
422
GetVpnUnderlyingAdapterType(const std::string & if_name)423 rtc::AdapterType AndroidNetworkMonitor::GetVpnUnderlyingAdapterType(
424 const std::string& if_name) {
425 auto iter = vpn_underlying_adapter_type_by_name_.find(if_name);
426 rtc::AdapterType type = (iter == vpn_underlying_adapter_type_by_name_.end())
427 ? rtc::ADAPTER_TYPE_UNKNOWN
428 : iter->second;
429 return type;
430 }
431
AndroidNetworkMonitorFactory()432 AndroidNetworkMonitorFactory::AndroidNetworkMonitorFactory()
433 : j_application_context_(nullptr) {}
434
AndroidNetworkMonitorFactory(JNIEnv * env,const JavaRef<jobject> & j_application_context)435 AndroidNetworkMonitorFactory::AndroidNetworkMonitorFactory(
436 JNIEnv* env,
437 const JavaRef<jobject>& j_application_context)
438 : j_application_context_(env, j_application_context) {}
439
440 AndroidNetworkMonitorFactory::~AndroidNetworkMonitorFactory() = default;
441
442 rtc::NetworkMonitorInterface*
CreateNetworkMonitor()443 AndroidNetworkMonitorFactory::CreateNetworkMonitor() {
444 return new AndroidNetworkMonitor(AttachCurrentThreadIfNeeded(),
445 j_application_context_);
446 }
447
NotifyConnectionTypeChanged(JNIEnv * env,const JavaRef<jobject> & j_caller)448 void AndroidNetworkMonitor::NotifyConnectionTypeChanged(
449 JNIEnv* env,
450 const JavaRef<jobject>& j_caller) {
451 OnNetworksChanged();
452 }
453
NotifyOfActiveNetworkList(JNIEnv * env,const JavaRef<jobject> & j_caller,const JavaRef<jobjectArray> & j_network_infos)454 void AndroidNetworkMonitor::NotifyOfActiveNetworkList(
455 JNIEnv* env,
456 const JavaRef<jobject>& j_caller,
457 const JavaRef<jobjectArray>& j_network_infos) {
458 std::vector<NetworkInformation> network_infos =
459 JavaToNativeVector<NetworkInformation>(env, j_network_infos,
460 &GetNetworkInformationFromJava);
461 SetNetworkInfos(network_infos);
462 }
463
NotifyOfNetworkConnect(JNIEnv * env,const JavaRef<jobject> & j_caller,const JavaRef<jobject> & j_network_info)464 void AndroidNetworkMonitor::NotifyOfNetworkConnect(
465 JNIEnv* env,
466 const JavaRef<jobject>& j_caller,
467 const JavaRef<jobject>& j_network_info) {
468 NetworkInformation network_info =
469 GetNetworkInformationFromJava(env, j_network_info);
470 OnNetworkConnected(network_info);
471 }
472
NotifyOfNetworkDisconnect(JNIEnv * env,const JavaRef<jobject> & j_caller,jlong network_handle)473 void AndroidNetworkMonitor::NotifyOfNetworkDisconnect(
474 JNIEnv* env,
475 const JavaRef<jobject>& j_caller,
476 jlong network_handle) {
477 OnNetworkDisconnected(static_cast<NetworkHandle>(network_handle));
478 }
479
480 } // namespace jni
481 } // namespace webrtc
482