diff -r 000000000000 -r 6474c204b198 media/mtransport/nrinterfaceprioritizer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/media/mtransport/nrinterfaceprioritizer.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,189 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include +#include +#include "logging.h" +#include "nrinterfaceprioritizer.h" +#include "nsCOMPtr.h" + +MOZ_MTLOG_MODULE("mtransport") + +namespace { + +class LocalAddress { +public: + LocalAddress() + : key_(), + is_vpn_(-1), + estimated_speed_(-1), + type_preference_(-1) {} + + bool Init(const nr_local_addr& local_addr) { + char buf[MAXIFNAME + 41]; + int r = nr_transport_addr_fmt_ifname_addr_string(&local_addr.addr, buf, sizeof(buf)); + if (r) { + MOZ_MTLOG(PR_LOG_ERROR, "Error formatting interface address string."); + return false; + } + key_ = buf; + is_vpn_ = (local_addr.interface.type & NR_INTERFACE_TYPE_VPN) != 0 ? 1 : 0; + estimated_speed_ = local_addr.interface.estimated_speed; + type_preference_ = GetNetworkTypePreference(local_addr.interface.type); + return true; + } + + bool operator<(const LocalAddress& rhs) const { + // Interface that is "less" here is preferred. + // If type preferences are different, we should simply sort by + // |type_preference_|. + if (type_preference_ != rhs.type_preference_) { + return type_preference_ < rhs.type_preference_; + } + + // If type preferences are the same, the next thing we use to sort is vpn. + // If two LocalAddress are different in |is_vpn_|, the LocalAddress that is + // not in vpn gets priority. + if (is_vpn_ != rhs.is_vpn_) { + return is_vpn_ < rhs.is_vpn_; + } + + // Compare estimated speed. + if (estimated_speed_ != rhs.estimated_speed_) { + return estimated_speed_ > rhs.estimated_speed_; + } + + // All things above are the same, we can at least sort with key. + return key_ < rhs.key_; + } + + const std::string& GetKey() const { + return key_; + } + +private: + // Getting the preference corresponding to a type. Getting lower number here + // means the type of network is preferred. + static inline int GetNetworkTypePreference(int type) { + if (type & NR_INTERFACE_TYPE_WIRED) { + return 1; + } + if (type & NR_INTERFACE_TYPE_WIFI) { + return 2; + } + if (type & NR_INTERFACE_TYPE_MOBILE) { + return 3; + } + return 4; + } + + std::string key_; + int is_vpn_; + int estimated_speed_; + int type_preference_; +}; + +class InterfacePrioritizer { +public: + InterfacePrioritizer() + : local_addrs_(), + preference_map_(), + sorted_(false) {} + + int add(const nr_local_addr *iface) { + LocalAddress addr; + if (!addr.Init(*iface)) { + return R_FAILED; + } + std::pair::iterator, bool> r = + local_addrs_.insert(addr); + if (!r.second) { + return R_ALREADY; // This address is already in the set. + } + sorted_ = false; + return 0; + } + + int sort() { + UCHAR tmp_pref = 127; + preference_map_.clear(); + for (std::set::iterator i = local_addrs_.begin(); + i != local_addrs_.end(); ++i) { + if (tmp_pref == 0) { + return R_FAILED; + } + preference_map_.insert(make_pair(i->GetKey(), tmp_pref--)); + } + sorted_ = true; + return 0; + } + + int getPreference(const char *key, UCHAR *pref) { + if (!sorted_) { + return R_FAILED; + } + std::map::iterator i = preference_map_.find(key); + if (i == preference_map_.end()) { + return R_NOT_FOUND; + } + *pref = i->second; + return 0; + } + +private: + std::set local_addrs_; + std::map preference_map_; + bool sorted_; +}; + +} // anonymous namespace + +static int add_interface(void *obj, nr_local_addr *iface) { + InterfacePrioritizer *ip = static_cast(obj); + return ip->add(iface); +} + +static int get_priority(void *obj, const char *key, UCHAR *pref) { + InterfacePrioritizer *ip = static_cast(obj); + return ip->getPreference(key, pref); +} + +static int sort_preference(void *obj) { + InterfacePrioritizer *ip = static_cast(obj); + return ip->sort(); +} + +static int destroy(void **objp) { + if (!objp || !*objp) { + return 0; + } + + InterfacePrioritizer *ip = static_cast(*objp); + *objp = 0; + delete ip; + + return 0; +} + +static nr_interface_prioritizer_vtbl priorizer_vtbl = { + add_interface, + get_priority, + sort_preference, + destroy +}; + +namespace mozilla { + +nr_interface_prioritizer* CreateInterfacePrioritizer() { + nr_interface_prioritizer *ip; + int r = nr_interface_prioritizer_create_int(new InterfacePrioritizer(), + &priorizer_vtbl, + &ip); + if (r != 0) { + return nullptr; + } + return ip; +} + +} // namespace mozilla