// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //////////////////////////////////////////////////////////////////////////////// // Threading considerations: // // This class is designed to meet various threading guarantees starting from the // ones imposed by NetworkChangeNotifier: // - The notifier can be constructed on any thread. // - GetCurrentConnectionType() can be called on any thread. // // The fact that this implementation of NetworkChangeNotifier is backed by a // Java side singleton class (see NetworkChangeNotifier.java) adds another // threading constraint: // - The calls to the Java side (stateful) object must be performed from a // single thread. This object happens to be a singleton which is used on the // application side on the main thread. Therefore all the method calls from // the native NetworkChangeNotifierAndroid class to its Java counterpart are // performed on the main thread. // // This leads to a design involving the following native classes: // 1) NetworkChangeNotifierFactoryAndroid ('factory') // 2) NetworkChangeNotifierDelegateAndroid ('delegate') // 3) NetworkChangeNotifierAndroid ('notifier') // // The factory constructs and owns the delegate. The factory is constructed and // destroyed on the main thread which makes it construct and destroy the // delegate on the main thread too. This guarantees that the calls to the Java // side are performed on the main thread. // Note that after the factory's construction, the factory's creation method can // be called from any thread since the delegate's construction (performing the // JNI calls) already happened on the main thread (when the factory was // constructed). // //////////////////////////////////////////////////////////////////////////////// // Propagation of network change notifications: // // When the factory is requested to create a new instance of the notifier, the // factory passes the delegate to the notifier (without transferring ownership). // Note that there is a one-to-one mapping between the factory and the // delegate as explained above. But the factory naturally creates multiple // instances of the notifier. That means that there is a one-to-many mapping // between delegate and notifier (i.e. a single delegate can be shared by // multiple notifiers). // At construction the notifier (which is also an observer) subscribes to // notifications fired by the delegate. These notifications, received by the // delegate (and forwarded to the notifier(s)), are sent by the Java side // notifier (see NetworkChangeNotifier.java) and are initiated by the Android // platform. // Notifications from the Java side always arrive on the main thread. The // delegate then forwards these notifications to the threads of each observer // (network change notifier). The network change notifier then processes the // state change, and notifies each of its observers on their threads. // // This can also be seen as: // Android platform -> NetworkChangeNotifier (Java) -> // NetworkChangeNotifierDelegateAndroid -> NetworkChangeNotifierAndroid. #include "net/android/network_change_notifier_android.h" #include #include #include "base/android/build_info.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/metrics/histogram_macros.h" #include "base/task/sequenced_task_runner.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/threading/thread.h" #include "net/base/address_tracker_linux.h" namespace net { // Expose handles::kInvalidNetworkHandle out to Java as NetId.INVALID. The // notion of a NetID is an Android framework one, see android.net.Network.netId. // NetworkChangeNotifierAndroid implements handles::NetworkHandle to simply be // the NetID. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net enum NetId { // Cannot use |handles::kInvalidNetworkHandle| here as the Java generator // fails, instead enforce their equality with CHECK in // NetworkChangeNotifierAndroid(). INVALID = -1 }; // Thread on which we can run DnsConfigService, which requires a TYPE_IO // message loop to monitor /system/etc/hosts. class NetworkChangeNotifierAndroid::BlockingThreadObjects { public: BlockingThreadObjects() : address_tracker_( base::DoNothing(), base::DoNothing(), // We're only interested in tunnel interface changes. base::BindRepeating(NotifyNetworkChangeNotifierObservers), std::unordered_set()) {} BlockingThreadObjects(const BlockingThreadObjects&) = delete; BlockingThreadObjects& operator=(const BlockingThreadObjects&) = delete; void Init() { address_tracker_.Init(); } static void NotifyNetworkChangeNotifierObservers() { NetworkChangeNotifier::NotifyObserversOfIPAddressChange(); NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange(); } private: // Used to detect tunnel state changes. internal::AddressTrackerLinux address_tracker_; }; NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() { ClearGlobalPointer(); delegate_->UnregisterObserver(this); } NetworkChangeNotifier::ConnectionType NetworkChangeNotifierAndroid::GetCurrentConnectionType() const { return delegate_->GetCurrentConnectionType(); } NetworkChangeNotifier::ConnectionCost NetworkChangeNotifierAndroid::GetCurrentConnectionCost() { return delegate_->GetCurrentConnectionCost(); } NetworkChangeNotifier::ConnectionSubtype NetworkChangeNotifierAndroid::GetCurrentConnectionSubtype() const { return delegate_->GetCurrentConnectionSubtype(); } void NetworkChangeNotifierAndroid::GetCurrentMaxBandwidthAndConnectionType( double* max_bandwidth_mbps, ConnectionType* connection_type) const { delegate_->GetCurrentMaxBandwidthAndConnectionType(max_bandwidth_mbps, connection_type); } void NetworkChangeNotifierAndroid::ForceNetworkHandlesSupportedForTesting() { force_network_handles_supported_for_testing_ = true; } bool NetworkChangeNotifierAndroid::AreNetworkHandlesCurrentlySupported() const { // Notifications for API using handles::NetworkHandles and querying using // handles::NetworkHandles only implemented for Android versions >= L. return force_network_handles_supported_for_testing_ || (base::android::BuildInfo::GetInstance()->sdk_int() >= base::android::SDK_VERSION_LOLLIPOP && !delegate_->RegisterNetworkCallbackFailed()); } void NetworkChangeNotifierAndroid::GetCurrentConnectedNetworks( NetworkChangeNotifier::NetworkList* networks) const { delegate_->GetCurrentlyConnectedNetworks(networks); } NetworkChangeNotifier::ConnectionType NetworkChangeNotifierAndroid::GetCurrentNetworkConnectionType( handles::NetworkHandle network) const { return delegate_->GetNetworkConnectionType(network); } handles::NetworkHandle NetworkChangeNotifierAndroid::GetCurrentDefaultNetwork() const { return delegate_->GetCurrentDefaultNetwork(); } void NetworkChangeNotifierAndroid::OnConnectionTypeChanged() { BlockingThreadObjects::NotifyNetworkChangeNotifierObservers(); } void NetworkChangeNotifierAndroid::OnConnectionCostChanged() { NetworkChangeNotifier::NotifyObserversOfConnectionCostChange(); } void NetworkChangeNotifierAndroid::OnMaxBandwidthChanged( double max_bandwidth_mbps, ConnectionType type) { NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps, type); } void NetworkChangeNotifierAndroid::OnNetworkConnected( handles::NetworkHandle network) { NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange( NetworkChangeType::kConnected, network); } void NetworkChangeNotifierAndroid::OnNetworkSoonToDisconnect( handles::NetworkHandle network) { NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange( NetworkChangeType::kSoonToDisconnect, network); } void NetworkChangeNotifierAndroid::OnNetworkDisconnected( handles::NetworkHandle network) { NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange( NetworkChangeType::kDisconnected, network); } void NetworkChangeNotifierAndroid::OnNetworkMadeDefault( handles::NetworkHandle network) { NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange( NetworkChangeType::kMadeDefault, network); } void NetworkChangeNotifierAndroid::OnDefaultNetworkActive() { NetworkChangeNotifier::NotifyObserversOfDefaultNetworkActive(); } NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid( NetworkChangeNotifierDelegateAndroid* delegate) : NetworkChangeNotifier(NetworkChangeCalculatorParamsAndroid()), delegate_(delegate), blocking_thread_objects_(nullptr, base::OnTaskRunnerDeleter(nullptr)) { static_assert(NetId::INVALID == handles::kInvalidNetworkHandle, "handles::kInvalidNetworkHandle doesn't match NetId::INVALID"); delegate_->RegisterObserver(this); // Since Android P, ConnectivityManager's signals include VPNs so we don't // need to use AddressTrackerLinux. if (base::android::BuildInfo::GetInstance()->sdk_int() < base::android::SDK_VERSION_P) { // |blocking_thread_objects_| will live on this runner. scoped_refptr blocking_thread_runner = base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); blocking_thread_objects_ = std::unique_ptr( new BlockingThreadObjects(), // Ensure |blocking_thread_objects_| lives on // |blocking_thread_runner| to prevent races where // NetworkChangeNotifierAndroid outlives // TaskEnvironment. https://crbug.com/938126 base::OnTaskRunnerDeleter(blocking_thread_runner)); blocking_thread_runner->PostTask( FROM_HERE, base::BindOnce(&BlockingThreadObjects::Init, // The Unretained pointer is safe here because it's // posted before the deleter can post. base::Unretained(blocking_thread_objects_.get()))); } } // static NetworkChangeNotifier::NetworkChangeCalculatorParams NetworkChangeNotifierAndroid::NetworkChangeCalculatorParamsAndroid() { NetworkChangeCalculatorParams params; // IPAddressChanged is produced immediately prior to ConnectionTypeChanged // so delay IPAddressChanged so they get merged with the following // ConnectionTypeChanged signal. params.ip_address_offline_delay_ = base::Seconds(1); params.ip_address_online_delay_ = base::Seconds(1); params.connection_type_offline_delay_ = base::Seconds(0); params.connection_type_online_delay_ = base::Seconds(0); return params; } bool NetworkChangeNotifierAndroid::IsDefaultNetworkActiveInternal() { return delegate_->IsDefaultNetworkActive(); } void NetworkChangeNotifierAndroid::DefaultNetworkActiveObserverAdded() { delegate_->DefaultNetworkActiveObserverAdded(); } void NetworkChangeNotifierAndroid::DefaultNetworkActiveObserverRemoved() { delegate_->DefaultNetworkActiveObserverRemoved(); } } // namespace net