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/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, jobject j_monitor, jlong j_native_monitor, |
| 401 jobjectArray j_network_infos) { |
| 402 AndroidNetworkMonitor* network_monitor = |
| 403 reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor); |
| 404 std::vector<NetworkInformation> network_infos; |
| 405 size_t num_networks = jni->GetArrayLength(j_network_infos); |
| 406 for (size_t i = 0; i < num_networks; ++i) { |
| 407 jobject j_network_info = jni->GetObjectArrayElement(j_network_infos, i); |
| 408 CHECK_EXCEPTION(jni) << "Error during GetObjectArrayElement"; |
| 409 network_infos.push_back(GetNetworkInformationFromJava(jni, j_network_info)); |
| 410 } |
| 411 network_monitor->SetNetworkInfos(network_infos); |
| 412 } |
| 413 |
| 414 JOW(void, NetworkMonitor_nativeNotifyOfNetworkConnect)( |
| 415 JNIEnv* jni, jobject j_monitor, jlong j_native_monitor, |
| 416 jobject j_network_info) { |
| 417 AndroidNetworkMonitor* network_monitor = |
| 418 reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor); |
| 419 NetworkInformation network_info = |
| 420 GetNetworkInformationFromJava(jni, j_network_info); |
| 421 network_monitor->OnNetworkConnected(network_info); |
| 422 } |
| 423 |
| 424 JOW(void, NetworkMonitor_nativeNotifyOfNetworkDisconnect)( |
| 425 JNIEnv* jni, jobject j_monitor, jlong j_native_monitor, |
| 426 jlong network_handle) { |
| 427 AndroidNetworkMonitor* network_monitor = |
| 428 reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor); |
| 429 network_monitor->OnNetworkDisconnected( |
| 430 static_cast<NetworkHandle>(network_handle)); |
| 431 } |
| 432 |
| 433 } // namespace webrtc_jni |
OLD | NEW |