blob: 20094bb746e675ad5e48ddf7334de8cc638a4392 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/network_change_notifier_delegate_android.h"
#include "base/android/jni_array.h"
#include "base/logging.h"
#include "jni/NetworkChangeNotifier_jni.h"
#include "net/android/network_change_notifier_android.h"
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace net {
namespace {
// Converts a Java side connection type (integer) to
// the native side NetworkChangeNotifier::ConnectionType.
NetworkChangeNotifier::ConnectionType ConvertConnectionType(
jint connection_type) {
switch (connection_type) {
case NetworkChangeNotifier::CONNECTION_UNKNOWN:
case NetworkChangeNotifier::CONNECTION_ETHERNET:
case NetworkChangeNotifier::CONNECTION_WIFI:
case NetworkChangeNotifier::CONNECTION_2G:
case NetworkChangeNotifier::CONNECTION_3G:
case NetworkChangeNotifier::CONNECTION_4G:
case NetworkChangeNotifier::CONNECTION_NONE:
case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
break;
default:
NOTREACHED() << "Unknown connection type received: " << connection_type;
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
return static_cast<NetworkChangeNotifier::ConnectionType>(connection_type);
}
// Converts a Java side connection type (integer) to
// the native side NetworkChangeNotifier::ConnectionType.
NetworkChangeNotifier::ConnectionSubtype ConvertConnectionSubtype(
jint subtype) {
DCHECK(subtype >= 0 && subtype <= NetworkChangeNotifier::SUBTYPE_LAST);
return static_cast<NetworkChangeNotifier::ConnectionSubtype>(subtype);
}
} // namespace
// static
void NetworkChangeNotifierDelegateAndroid::JavaLongArrayToNetworkMap(
JNIEnv* env,
const JavaRef<jlongArray>& long_array,
NetworkMap* network_map) {
std::vector<int64_t> int64_list;
base::android::JavaLongArrayToInt64Vector(env, long_array.obj(), &int64_list);
network_map->clear();
for (auto i = int64_list.begin(); i != int64_list.end(); ++i) {
NetworkChangeNotifier::NetworkHandle network_handle = *i;
CHECK(++i != int64_list.end());
(*network_map)[network_handle] = static_cast<ConnectionType>(*i);
}
}
NetworkChangeNotifierDelegateAndroid::NetworkChangeNotifierDelegateAndroid()
: observers_(new base::ObserverListThreadSafe<Observer>()),
java_network_change_notifier_(Java_NetworkChangeNotifier_init(
base::android::AttachCurrentThread())),
register_network_callback_failed_(
Java_NetworkChangeNotifier_registerNetworkCallbackFailed(
base::android::AttachCurrentThread(),
java_network_change_notifier_)) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_addNativeObserver(
env, java_network_change_notifier_, reinterpret_cast<intptr_t>(this));
SetCurrentConnectionType(
ConvertConnectionType(Java_NetworkChangeNotifier_getCurrentConnectionType(
env, java_network_change_notifier_)));
SetCurrentMaxBandwidth(
NetworkChangeNotifierAndroid::GetMaxBandwidthMbpsForConnectionSubtype(
GetCurrentConnectionSubtype()));
SetCurrentDefaultNetwork(Java_NetworkChangeNotifier_getCurrentDefaultNetId(
env, java_network_change_notifier_));
NetworkMap network_map;
ScopedJavaLocalRef<jlongArray> networks_and_types =
Java_NetworkChangeNotifier_getCurrentNetworksAndTypes(
env, java_network_change_notifier_);
JavaLongArrayToNetworkMap(env, networks_and_types, &network_map);
SetCurrentNetworksAndTypes(network_map);
}
NetworkChangeNotifierDelegateAndroid::~NetworkChangeNotifierDelegateAndroid() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
observers_->AssertEmpty();
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_removeNativeObserver(
env, java_network_change_notifier_, reinterpret_cast<intptr_t>(this));
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierDelegateAndroid::GetCurrentConnectionType() const {
base::AutoLock auto_lock(connection_lock_);
return connection_type_;
}
NetworkChangeNotifier::ConnectionSubtype
NetworkChangeNotifierDelegateAndroid::GetCurrentConnectionSubtype() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return ConvertConnectionSubtype(
Java_NetworkChangeNotifier_getCurrentConnectionSubtype(
base::android::AttachCurrentThread(), java_network_change_notifier_));
}
void NetworkChangeNotifierDelegateAndroid::
GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const {
base::AutoLock auto_lock(connection_lock_);
*connection_type = connection_type_;
*max_bandwidth_mbps = connection_max_bandwidth_;
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierDelegateAndroid::GetNetworkConnectionType(
NetworkChangeNotifier::NetworkHandle network) const {
base::AutoLock auto_lock(connection_lock_);
auto network_entry = network_map_.find(network);
if (network_entry == network_map_.end())
return ConnectionType::CONNECTION_UNKNOWN;
return network_entry->second;
}
NetworkChangeNotifier::NetworkHandle
NetworkChangeNotifierDelegateAndroid::GetCurrentDefaultNetwork() const {
base::AutoLock auto_lock(connection_lock_);
return default_network_;
}
void NetworkChangeNotifierDelegateAndroid::GetCurrentlyConnectedNetworks(
NetworkList* network_list) const {
network_list->clear();
base::AutoLock auto_lock(connection_lock_);
for (auto i : network_map_)
network_list->push_back(i.first);
}
void NetworkChangeNotifierDelegateAndroid::NotifyConnectionTypeChanged(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint new_connection_type,
jlong default_netid) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
const ConnectionType actual_connection_type = ConvertConnectionType(
new_connection_type);
SetCurrentConnectionType(actual_connection_type);
NetworkHandle default_network = default_netid;
if (default_network != GetCurrentDefaultNetwork()) {
SetCurrentDefaultNetwork(default_network);
bool default_exists;
{
base::AutoLock auto_lock(connection_lock_);
// |default_network| may be an invalid value (i.e. -1) in cases where
// the device is disconnected or when run on Android versions prior to L,
// in which case |default_exists| will correctly be false and no
// OnNetworkMadeDefault notification will be sent.
default_exists = network_map_.find(default_network) != network_map_.end();
}
// Android Lollipop had race conditions where CONNECTIVITY_ACTION intents
// were sent out before the network was actually made the default.
// Delay sending the OnNetworkMadeDefault notification until we are
// actually notified that the network connected in NotifyOfNetworkConnect.
if (default_exists) {
observers_->Notify(FROM_HERE, &Observer::OnNetworkMadeDefault,
default_network);
}
}
observers_->Notify(FROM_HERE, &Observer::OnConnectionTypeChanged);
}
jint NetworkChangeNotifierDelegateAndroid::GetConnectionType(JNIEnv*,
jobject) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetCurrentConnectionType();
}
void NetworkChangeNotifierDelegateAndroid::NotifyMaxBandwidthChanged(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint subtype) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
double new_max_bandwidth =
NetworkChangeNotifierAndroid::GetMaxBandwidthMbpsForConnectionSubtype(
ConvertConnectionSubtype(subtype));
SetCurrentMaxBandwidth(new_max_bandwidth);
observers_->Notify(FROM_HERE, &Observer::OnMaxBandwidthChanged,
new_max_bandwidth, GetCurrentConnectionType());
}
void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkConnect(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jlong net_id,
jint connection_type) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NetworkHandle network = net_id;
bool already_exists;
{
base::AutoLock auto_lock(connection_lock_);
already_exists = network_map_.find(network) != network_map_.end();
network_map_[network] = static_cast<ConnectionType>(connection_type);
}
// Android Lollipop would send many duplicate notifications.
// This was later fixed in Android Marshmallow.
// Deduplicate them here by avoiding sending duplicate notifications.
if (!already_exists) {
observers_->Notify(FROM_HERE, &Observer::OnNetworkConnected, network);
if (network == GetCurrentDefaultNetwork()) {
observers_->Notify(FROM_HERE, &Observer::OnNetworkMadeDefault, network);
}
}
}
void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkSoonToDisconnect(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jlong net_id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NetworkHandle network = net_id;
{
base::AutoLock auto_lock(connection_lock_);
if (network_map_.find(network) == network_map_.end())
return;
}
observers_->Notify(FROM_HERE, &Observer::OnNetworkSoonToDisconnect, network);
}
void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkDisconnect(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jlong net_id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NetworkHandle network = net_id;
{
base::AutoLock auto_lock(connection_lock_);
if (network == default_network_)
default_network_ = NetworkChangeNotifier::kInvalidNetworkHandle;
if (network_map_.erase(network) == 0)
return;
}
observers_->Notify(FROM_HERE, &Observer::OnNetworkDisconnected, network);
}
void NetworkChangeNotifierDelegateAndroid::NotifyPurgeActiveNetworkList(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jlongArray>& active_networks) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NetworkList active_network_list;
base::android::JavaLongArrayToInt64Vector(env, active_networks,
&active_network_list);
NetworkList disconnected_networks;
{
base::AutoLock auto_lock(connection_lock_);
for (auto i : network_map_) {
bool found = false;
for (auto j : active_network_list) {
if (j == i.first) {
found = true;
break;
}
}
if (!found) {
disconnected_networks.push_back(i.first);
}
}
}
for (auto disconnected_network : disconnected_networks)
NotifyOfNetworkDisconnect(env, obj, disconnected_network);
}
void NetworkChangeNotifierDelegateAndroid::AddObserver(
Observer* observer) {
observers_->AddObserver(observer);
}
void NetworkChangeNotifierDelegateAndroid::RemoveObserver(
Observer* observer) {
observers_->RemoveObserver(observer);
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentConnectionType(
ConnectionType new_connection_type) {
base::AutoLock auto_lock(connection_lock_);
connection_type_ = new_connection_type;
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentMaxBandwidth(
double max_bandwidth) {
base::AutoLock auto_lock(connection_lock_);
connection_max_bandwidth_ = max_bandwidth;
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentDefaultNetwork(
NetworkHandle default_network) {
base::AutoLock auto_lock(connection_lock_);
default_network_ = default_network;
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentNetworksAndTypes(
NetworkMap network_map) {
base::AutoLock auto_lock(connection_lock_);
network_map_ = network_map;
}
void NetworkChangeNotifierDelegateAndroid::SetOnline() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_forceConnectivityState(env, true);
}
void NetworkChangeNotifierDelegateAndroid::SetOffline() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_forceConnectivityState(env, false);
}
void NetworkChangeNotifierDelegateAndroid::FakeNetworkConnected(
NetworkChangeNotifier::NetworkHandle network,
ConnectionType type) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeNetworkConnected(env, network, type);
}
void NetworkChangeNotifierDelegateAndroid::FakeNetworkSoonToBeDisconnected(
NetworkChangeNotifier::NetworkHandle network) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeNetworkSoonToBeDisconnected(env, network);
}
void NetworkChangeNotifierDelegateAndroid::FakeNetworkDisconnected(
NetworkChangeNotifier::NetworkHandle network) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeNetworkDisconnected(env, network);
}
void NetworkChangeNotifierDelegateAndroid::FakePurgeActiveNetworkList(
NetworkChangeNotifier::NetworkList networks) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakePurgeActiveNetworkList(
env, base::android::ToJavaLongArray(env, networks));
}
void NetworkChangeNotifierDelegateAndroid::FakeDefaultNetwork(
NetworkChangeNotifier::NetworkHandle network,
ConnectionType type) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeDefaultNetwork(env, network, type);
}
void NetworkChangeNotifierDelegateAndroid::FakeConnectionSubtypeChanged(
ConnectionSubtype subtype) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeConnectionSubtypeChanged(env, subtype);
}
bool NetworkChangeNotifierDelegateAndroid::IsProcessBoundToNetwork() {
return Java_NetworkChangeNotifier_isProcessBoundToNetwork(
base::android::AttachCurrentThread());
}
} // namespace net