Asynchronous NetworkManager and various other fixes.

git-svn-id: http://libjingle.googlecode.com/svn/trunk@73 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/base/autodetectproxy.cc b/talk/base/autodetectproxy.cc
index ffcac26..f79926d 100644
--- a/talk/base/autodetectproxy.cc
+++ b/talk/base/autodetectproxy.cc
@@ -38,6 +38,15 @@
   PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN
 };
 
+static const int kSavedStringLimit = 128;
+
+static void SaveStringToStack(char *dst,
+                              const std::string &src,
+                              size_t dst_size) {
+  strncpy(dst, src.c_str(), dst_size - 1);
+  dst[dst_size - 1] = '\0';
+}
+
 AutoDetectProxy::AutoDetectProxy(const std::string& user_agent)
     : agent_(user_agent), socket_(NULL), next_(0) {
 }
@@ -75,7 +84,54 @@
   if (MSG_TIMEOUT == msg->message_id) {
     OnCloseEvent(socket_, ETIMEDOUT);
   } else {
+    // This must be the ST_MSG_WORKER_DONE message that deletes the
+    // AutoDetectProxy object. We have observed crashes within this stack that
+    // seem to be highly reproducible for a small subset of users and thus are
+    // probably correlated with a specific proxy setting, so copy potentially
+    // relevant information onto the stack to make it available in Windows
+    // minidumps.
+
+    // Save the user agent and the number of auto-detection passes that we
+    // needed.
+    char agent[kSavedStringLimit];
+    SaveStringToStack(agent, agent_, sizeof agent);
+
+    int next = next_;
+
+    // Now the detected proxy config (minus the password field, which could be
+    // sensitive).
+    ProxyType type = proxy().type;
+
+    char address_hostname[kSavedStringLimit];
+    SaveStringToStack(address_hostname,
+                      proxy().address.hostname(),
+                      sizeof address_hostname);
+
+    uint32 address_ip = proxy().address.ip();
+
+    uint16 address_port = proxy().address.port();
+
+    char autoconfig_url[kSavedStringLimit];
+    SaveStringToStack(autoconfig_url,
+                      proxy().autoconfig_url,
+                      sizeof autoconfig_url);
+
+    bool autodetect = proxy().autodetect;
+
+    char bypass_list[kSavedStringLimit];
+    SaveStringToStack(bypass_list, proxy().bypass_list, sizeof bypass_list);
+
+    char username[kSavedStringLimit];
+    SaveStringToStack(username, proxy().username, sizeof username);
+
     SignalThread::OnMessage(msg);
+
+    // Log the gathered data at a log level that will never actually be enabled
+    // so that the compiler is forced to retain the data on the stack.
+    LOG(LS_SENSITIVE) << agent << " " << next << " " << type << " "
+                      << address_hostname << " " << address_ip << " "
+                      << address_port << " " << autoconfig_url << " "
+                      << autodetect << " " << bypass_list << " " << username;
   }
 }
 
diff --git a/talk/base/autodetectproxy.h b/talk/base/autodetectproxy.h
index 6bb2a4b..121737f 100644
--- a/talk/base/autodetectproxy.h
+++ b/talk/base/autodetectproxy.h
@@ -30,6 +30,7 @@
 
 #include <string>
 
+#include "talk/base/constructormagic.h"
 #include "talk/base/cryptstring.h"
 #include "talk/base/proxyinfo.h"
 #include "talk/base/signalthread.h"
@@ -83,6 +84,8 @@
   ProxyInfo proxy_;
   AsyncSocket* socket_;
   int next_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AutoDetectProxy);
 };
 
 }  // namespace talk_base
diff --git a/talk/base/messagehandler.h b/talk/base/messagehandler.h
index eb7db9b..bf7a4c2 100644
--- a/talk/base/messagehandler.h
+++ b/talk/base/messagehandler.h
@@ -25,8 +25,10 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef TALK_BASE_MESSAGEHANDLER_H__
-#define TALK_BASE_MESSAGEHANDLER_H__
+#ifndef TALK_BASE_MESSAGEHANDLER_H_
+#define TALK_BASE_MESSAGEHANDLER_H_
+
+#include "talk/base/constructormagic.h"
 
 namespace talk_base {
 
@@ -35,12 +37,18 @@
 // Messages get dispatched to a MessageHandler
 
 class MessageHandler {
-public:
+ public:
   virtual ~MessageHandler();
 
   virtual void OnMessage(Message* msg) = 0;
+
+ protected:
+  MessageHandler() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MessageHandler);
 };
 
 } // namespace talk_base
 
-#endif // TALK_BASE_MESSAGEHANDLER_H__
+#endif // TALK_BASE_MESSAGEHANDLER_H_
diff --git a/talk/base/messagequeue.h b/talk/base/messagequeue.h
index 4d470df..54d4860 100644
--- a/talk/base/messagequeue.h
+++ b/talk/base/messagequeue.h
@@ -35,6 +35,7 @@
 #include <vector>
 
 #include "talk/base/basictypes.h"
+#include "talk/base/constructormagic.h"
 #include "talk/base/criticalsection.h"
 #include "talk/base/messagehandler.h"
 #include "talk/base/scoped_ptr.h"
@@ -238,6 +239,9 @@
   PriorityQueue dmsgq_;
   uint32 dmsgq_next_num_;
   CriticalSection crit_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MessageQueue);
 };
 
 }  // namespace talk_base
diff --git a/talk/base/network.cc b/talk/base/network.cc
index e5dffc1..2feb361 100644
--- a/talk/base/network.cc
+++ b/talk/base/network.cc
@@ -30,7 +30,6 @@
 #endif
 
 #include "talk/base/network.h"
-#include "talk/base/stream.h"
 
 #ifdef POSIX
 #include <sys/socket.h>
@@ -47,162 +46,23 @@
 #endif
 
 #include <algorithm>
-#include <cassert>
-#include <cfloat>
-#include <cmath>
 #include <cstdio>
-#include <cstring>
-#include <sstream>
 
 #include "talk/base/host.h"
 #include "talk/base/logging.h"
 #include "talk/base/scoped_ptr.h"
 #include "talk/base/socket.h"  // includes something that makes windows happy
-#include "talk/base/stringencode.h"
-#include "talk/base/time.h"
-
-namespace {
-
-const double kAlpha = 0.5;  // weight for data infinitely far in the past
-const double kHalfLife = 2000;  // half life of exponential decay (in ms)
-const double kLog2 = 0.693147180559945309417;
-const double kLambda = kLog2 / kHalfLife;
-
-// assume so-so quality unless data says otherwise
-const double kDefaultQuality = talk_base::QUALITY_FAIR;
-
-typedef std::map<std::string, std::string> StrMap;
-
-void BuildMap(const StrMap& map, std::string& str) {
-  str.append("{");
-  bool first = true;
-  for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) {
-    if (!first) str.append(",");
-    str.append(i->first);
-    str.append("=");
-    str.append(i->second);
-    first = false;
-  }
-  str.append("}");
-}
-
-void ParseCheck(std::istringstream& ist, char ch) {
-  if (ist.get() != ch)
-    LOG(LERROR) << "Expecting '" << ch << "'";
-}
-
-std::string ParseString(std::istringstream& ist) {
-  std::string str;
-  int count = 0;
-  while (ist) {
-    char ch = ist.peek();
-    if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) {
-      break;
-    } else if (ch == '{') {
-      count += 1;
-    } else if (ch == '}') {
-      count -= 1;
-      if (count < 0)
-        LOG(LERROR) << "mismatched '{' and '}'";
-    }
-    str.append(1, static_cast<char>(ist.get()));
-  }
-  return str;
-}
-
-void ParseMap(const std::string& str, StrMap& map) {
-  if (str.size() == 0)
-    return;
-  std::istringstream ist(str);
-  ParseCheck(ist, '{');
-  for (;;) {
-    std::string key = ParseString(ist);
-    ParseCheck(ist, '=');
-    std::string val = ParseString(ist);
-    map[key] = val;
-    if (ist.peek() == ',')
-      ist.get();
-    else
-      break;
-  }
-  ParseCheck(ist, '}');
-  if (ist.rdbuf()->in_avail() != 0)
-    LOG(LERROR) << "Unexpected characters at end";
-}
-
-}  // namespace
+#include "talk/base/stream.h"
+#include "talk/base/thread.h"
 
 namespace talk_base {
 
-NetworkManager::~NetworkManager() {
-  for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i)
-    delete i->second;
-}
+namespace {
 
-bool NetworkManager::GetNetworks(std::vector<Network*>* result) {
-  std::vector<Network*> list;
-  if (!EnumNetworks(false, &list)) {
-    return false;
-  }
+const uint32 kUpdateNetworksMessage = 1;
 
-  for (uint32 i = 0; i < list.size(); ++i) {
-    NetworkMap::iterator iter = networks_.find(list[i]->name());
-
-    Network* network;
-    if (iter == networks_.end()) {
-      network = list[i];
-    } else {
-      network = iter->second;
-      network->set_ip(list[i]->ip());
-      network->set_gateway_ip(list[i]->gateway_ip());
-      delete list[i];
-    }
-
-    networks_[network->name()] = network;
-    result->push_back(network);
-  }
-  return true;
-}
-
-void NetworkManager::DumpNetworks(bool include_ignored) {
-  std::vector<Network*> list;
-  EnumNetworks(include_ignored, &list);
-  LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:";
-  for (size_t i = 0; i < list.size(); ++i) {
-    const Network* network = list[i];
-    if (!network->ignored() || include_ignored) {
-      LOG(LS_INFO) << network->ToString() << ": " << network->description()
-                   << ", Gateway="
-                   << SocketAddress::IPToString(network->gateway_ip())
-                   << ((network->ignored()) ? ", Ignored" : "");
-    }
-  }
-}
-
-std::string NetworkManager::GetState() const {
-  StrMap map;
-  for (NetworkMap::const_iterator i = networks_.begin();
-       i != networks_.end(); ++i)
-    map[i->first] = i->second->GetState();
-
-  std::string str;
-  BuildMap(map, str);
-  return str;
-}
-
-void NetworkManager::SetState(const std::string& str) {
-  StrMap map;
-  ParseMap(str, map);
-
-  for (StrMap::iterator i = map.begin(); i != map.end(); ++i) {
-    std::string name = i->first;
-    std::string state = i->second;
-
-    Network* network = new Network(name, "", 0, 0);
-    network->SetState(state);
-    networks_[name] = network;
-  }
-}
+// Fetch list of networks every two seconds.
+const int kNetworksUpdateIntervalMs = 2000;
 
 #ifdef POSIX
 // Gets the default gateway for the specified interface.
@@ -215,10 +75,10 @@
 
   uint32 gateway_ip = 0;
 
-  FileStream fs;
+  talk_base::FileStream fs;
   if (fs.Open("/proc/net/route", "r", NULL)) {
     std::string line;
-    while (fs.ReadLine(&line) == SR_SUCCESS && gateway_ip == 0) {
+    while (fs.ReadLine(&line) == talk_base::SR_SUCCESS && gateway_ip == 0) {
       char iface[16];
       unsigned int ip, gw;
       if (sscanf(line.c_str(), "%7s %8X %8X", iface, &ip, &gw) == 3 &&
@@ -230,10 +90,91 @@
 
   return gateway_ip;
 }
+#endif  // POSIX
 
+bool CompareNetworks(const Network* a, const Network* b) {
+  return a->name() < b->name();
+}
 
-bool NetworkManager::CreateNetworks(bool include_ignored,
-                                    std::vector<Network*>* networks) {
+}  // namespace
+
+NetworkManager::NetworkManager() {
+}
+
+NetworkManager::~NetworkManager() {
+}
+
+NetworkManagerBase::NetworkManagerBase() {
+}
+
+NetworkManagerBase::~NetworkManagerBase() {
+  for (NetworkMap::iterator i = networks_map_.begin();
+       i != networks_map_.end(); ++i) {
+    delete i->second;
+  }
+}
+
+void NetworkManagerBase::GetNetworks(NetworkList* result) const {
+  *result = networks_;
+}
+
+void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks,
+                                          bool force_notification) {
+  // Sort the list so that we can detect when it changes.
+  NetworkList list(new_networks);
+  std::sort(list.begin(), list.end(), CompareNetworks);
+
+  bool changed = false;
+
+  if (networks_.size() != list.size())
+    changed = true;
+
+  networks_.resize(list.size());
+
+  for (uint32 i = 0; i < list.size(); ++i) {
+    NetworkMap::iterator iter = networks_map_.find(list[i]->name());
+
+    Network* network;
+    if (iter == networks_map_.end()) {
+      // That's a new network, add it to the map.
+      network = list[i];
+      networks_map_[network->name()] = network;
+    } else {
+      network = iter->second;
+      if (network->ip() != list[i]->ip()) {
+        changed = true;
+        network->set_ip(list[i]->ip());
+      }
+
+      if (network->gateway_ip() != list[i]->gateway_ip()) {
+        changed = true;
+        network->set_gateway_ip(list[i]->gateway_ip());
+      }
+
+      delete list[i];
+    }
+
+    if (!changed && networks_[i]->name() != network->name())
+      changed = true;
+
+    networks_[i] = network;
+  }
+
+  if (changed || force_notification)
+    SignalNetworksChanged();
+}
+
+BasicNetworkManager::BasicNetworkManager()
+    : thread_(NULL),
+      started_(false) {
+}
+
+BasicNetworkManager::~BasicNetworkManager() {
+}
+
+#ifdef POSIX
+bool BasicNetworkManager::CreateNetworks(bool include_ignored,
+                                         NetworkList* networks) {
   int fd;
   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
     LOG_ERR(LERROR) << "socket";
@@ -248,7 +189,7 @@
     LOG_ERR(LERROR) << "ioctl";
     return false;
   }
-  assert(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq)));
+  ASSERT(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq)));
 
   struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf);
   struct ifreq* end =
@@ -283,8 +224,8 @@
 #endif  // POSIX
 
 #ifdef WIN32
-bool NetworkManager::CreateNetworks(bool include_ignored,
-                                    std::vector<Network*>* networks) {
+bool BasicNetworkManager::CreateNetworks(bool include_ignored,
+                                         NetworkList* networks) {
   IP_ADAPTER_INFO info_temp;
   ULONG len = 0;
 
@@ -332,7 +273,7 @@
 }
 #endif  // WIN32
 
-bool NetworkManager::IsIgnoredNetwork(const Network& network) {
+bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) {
 #ifdef POSIX
   // Ignore local networks (lo, lo0, etc)
   // Also filter out VMware interfaces, typically named vmnet1 and vmnet8
@@ -354,57 +295,65 @@
   return (network.ip() < 0x01000000);
 }
 
-bool NetworkManager::EnumNetworks(bool include_ignored,
-                                  std::vector<Network*>* result) {
-  return CreateNetworks(include_ignored, result);
+void BasicNetworkManager::StartUpdating() {
+  if (started_) {
+    sent_first_update_ = false;
+    return;
+  }
+
+  thread_ = Thread::Current();
+  started_ = true;
+  sent_first_update_ = false;
+  thread_->Post(this, kUpdateNetworksMessage);
 }
 
+void BasicNetworkManager::StopUpdating() {
+  ASSERT(Thread::Current() == thread_);
+  started_ = false;
+}
+
+void BasicNetworkManager::OnMessage(Message* msg) {
+  ASSERT(msg->message_id == kUpdateNetworksMessage);
+  DoUpdateNetworks();
+}
+
+void BasicNetworkManager::DoUpdateNetworks() {
+  if (!started_)
+    return;
+
+  ASSERT(Thread::Current() == thread_);
+
+  NetworkList list;
+  if (!CreateNetworks(false, &list)) {
+    SignalError();
+  } else {
+    MergeNetworkList(list, !sent_first_update_);
+    sent_first_update_ = true;
+  }
+
+  thread_->PostDelayed(kNetworksUpdateIntervalMs, this, kUpdateNetworksMessage);
+}
+
+void BasicNetworkManager::DumpNetworks(bool include_ignored) {
+  NetworkList list;
+  CreateNetworks(include_ignored, &list);
+  LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:";
+  for (size_t i = 0; i < list.size(); ++i) {
+    const Network* network = list[i];
+    if (!network->ignored() || include_ignored) {
+      LOG(LS_INFO) << network->ToString() << ": " << network->description()
+                   << ", Gateway="
+                   << SocketAddress::IPToString(network->gateway_ip())
+                   << ((network->ignored()) ? ", Ignored" : "");
+    }
+  }
+}
 
 Network::Network(const std::string& name, const std::string& desc,
                  uint32 ip, uint32 gateway_ip)
     : name_(name), description_(desc), ip_(ip), gateway_ip_(gateway_ip),
       ignored_(false), uniform_numerator_(0), uniform_denominator_(0),
-      exponential_numerator_(0), exponential_denominator_(0),
-      quality_(kDefaultQuality) {
-  last_data_time_ = Time();
-
-  // TODO: seed the historical data with one data point based
-  // on the link speed metric from XP (4.0 if < 50, 3.0 otherwise).
-}
-
-void Network::StartSession(NetworkSession* session) {
-  assert(std::find(sessions_.begin(), sessions_.end(), session) ==
-         sessions_.end());
-  sessions_.push_back(session);
-}
-
-void Network::StopSession(NetworkSession* session) {
-  SessionList::iterator iter =
-      std::find(sessions_.begin(), sessions_.end(), session);
-  if (iter != sessions_.end())
-    sessions_.erase(iter);
-}
-
-void Network::EstimateQuality() {
-  uint32 now = Time();
-
-  // Add new data points for the current time.
-  for (uint32 i = 0; i < sessions_.size(); ++i) {
-    if (sessions_[i]->HasQuality())
-      AddDataPoint(now, sessions_[i]->GetCurrentQuality());
-  }
-
-  // Construct the weighted average using both uniform and exponential weights.
-
-  double exp_shift = exp(-kLambda * (now - last_data_time_));
-  double numerator = uniform_numerator_ + exp_shift * exponential_numerator_;
-  double denominator = uniform_denominator_ + exp_shift *
-                       exponential_denominator_;
-
-  if (denominator < DBL_EPSILON)
-    quality_ = kDefaultQuality;
-  else
-    quality_ = numerator / denominator;
+      exponential_numerator_(0), exponential_denominator_(0) {
 }
 
 std::string Network::ToString() const {
@@ -416,41 +365,4 @@
   return ss.str();
 }
 
-void Network::AddDataPoint(uint32 time, double quality) {
-  uniform_numerator_ += kAlpha * quality;
-  uniform_denominator_ += kAlpha;
-
-  double exp_shift = exp(-kLambda * (time - last_data_time_));
-  exponential_numerator_ = (1 - kAlpha) * quality + exp_shift *
-                           exponential_numerator_;
-  exponential_denominator_ = (1 - kAlpha) + exp_shift *
-                             exponential_denominator_;
-
-  last_data_time_ = time;
-}
-
-std::string Network::GetState() const {
-  StrMap map;
-  map["lt"] = talk_base::ToString<uint32>(last_data_time_);
-  map["un"] = talk_base::ToString<double>(uniform_numerator_);
-  map["ud"] = talk_base::ToString<double>(uniform_denominator_);
-  map["en"] = talk_base::ToString<double>(exponential_numerator_);
-  map["ed"] = talk_base::ToString<double>(exponential_denominator_);
-
-  std::string str;
-  BuildMap(map, str);
-  return str;
-}
-
-void Network::SetState(const std::string& str) {
-  StrMap map;
-  ParseMap(str, map);
-
-  last_data_time_ = FromString<uint32>(map["lt"]);
-  uniform_numerator_ = FromString<double>(map["un"]);
-  uniform_denominator_ = FromString<double>(map["ud"]);
-  exponential_numerator_ = FromString<double>(map["en"]);
-  exponential_denominator_ = FromString<double>(map["ed"]);
-}
-
 }  // namespace talk_base
diff --git a/talk/base/network.h b/talk/base/network.h
index 8153e54..4b315c3 100644
--- a/talk/base/network.h
+++ b/talk/base/network.h
@@ -34,50 +34,105 @@
 #include <vector>
 
 #include "talk/base/basictypes.h"
+#include "talk/base/messagehandler.h"
+#include "talk/base/sigslot.h"
 
 namespace talk_base {
 
 class Network;
 class NetworkSession;
+class Thread;
 
-// Keeps track of the available network interfaces over time so that quality
-// information can be aggregated and recorded.
+// Generic network manager interface. It provides list of local
+// networks.
 class NetworkManager {
  public:
+  typedef std::vector<Network*> NetworkList;
+
+  NetworkManager();
   virtual ~NetworkManager();
 
-  // Updates and returns the current list of networks available on this machine.
-  // This version will make sure that repeated calls return the same object for
-  // a given network, so that quality is tracked appropriately.
-  // Does not include ignored networks.
-  bool GetNetworks(std::vector<Network*>* networks);
+  // Called when network list is updated.
+  sigslot::signal0<> SignalNetworksChanged;
 
-  // Logs the available networks.
-  void DumpNetworks(bool include_ignored);
+  // Indicates a failure when getting list of network interfaces.
+  sigslot::signal0<> SignalError;
 
-  // Reads and writes the state of the quality database in a string format.
-  std::string GetState() const;
-  void SetState(const std::string& str);
+  // Start/Stop monitoring of network interfaces
+  // list. SignalNetworksChanged or SignalError is emitted immidiately
+  // after StartUpdating() is called. After that SignalNetworksChanged
+  // is emitted wheneven list of networks changes.
+  virtual void StartUpdating() = 0;
+  virtual void StopUpdating() = 0;
 
-  // Creates a network object for each network available on the machine.
-  static bool CreateNetworks(bool include_ignored,
-                             std::vector<Network*>* networks);
-  // Determines if a network should be ignored.
-  static bool IsIgnoredNetwork(const Network& network);
+  // Returns the current list of networks available on this machine.
+  // UpdateNetworks() must be called before this method is called.
+  // It makes sure that repeated calls return the same object for a
+  // given network, so that quality is tracked appropriately. Does not
+  // include ignored networks.
+  virtual void GetNetworks(NetworkList* networks) const = 0;
+};
+
+// Base class for NetworkManager implementations.
+class NetworkManagerBase : public NetworkManager {
+ public:
+  NetworkManagerBase();
+  virtual ~NetworkManagerBase();
+
+  virtual void GetNetworks(std::vector<Network*>* networks) const;
 
  protected:
-  // Fills the supplied list with all usable networks. Overrideable.
-  virtual bool EnumNetworks(bool include_ignored,
-                            std::vector<Network*>* networks);
+  // Updates |networks_| with the networks listed in |list|. If
+  // |network_map_| already has a Network object for a network listed
+  // in the |list| then it is reused. Accept ownership of the Network
+  // objects in the |list|. SignalNetworkListUpdated is emitted if
+  // there is a change in network configuration or
+  // |force_notification| is set to true.
+  void MergeNetworkList(const NetworkList& list, bool force_notification);
 
  private:
   typedef std::map<std::string, Network*> NetworkMap;
 
-  NetworkMap networks_;
+  void DoUpdateNetworks();
+
+  NetworkList networks_;
+  NetworkMap networks_map_;
+};
+
+// Basic implementation of the NetworkManager interface that gets list
+// of networks using OS APIs.
+class BasicNetworkManager : public NetworkManagerBase,
+                            public MessageHandler {
+ public:
+  BasicNetworkManager();
+  virtual ~BasicNetworkManager();
+
+  virtual void StartUpdating();
+  virtual void StopUpdating();
+
+  // Logs the available networks.
+  static void DumpNetworks(bool include_ignored);
+
+  // MessageHandler interface.
+  virtual void OnMessage(Message* msg);
+
+ protected:
+  // Creates a network object for each network available on the machine.
+  static bool CreateNetworks(bool include_ignored, NetworkList* networks);
+  // Determines if a network should be ignored.
+  static bool IsIgnoredNetwork(const Network& network);
+
+ private:
+  friend class NetworkTest;
+
+  void DoUpdateNetworks();
+
+  Thread* thread_;
+  bool started_;
+  bool sent_first_update_;
 };
 
 // Represents a Unix-type network interface, with a name and single address.
-// It also includes the ability to track and estimate quality.
 class Network {
  public:
   Network(const std::string& name, const std::string& description,
@@ -104,18 +159,6 @@
   bool ignored() const { return ignored_; }
   void set_ignored(bool ignored) { ignored_ = ignored; }
 
-  // Updates the list of sessions that are ongoing.
-  void StartSession(NetworkSession* session);
-  void StopSession(NetworkSession* session);
-
-  // Re-computes the estimate of near-future quality based on the information
-  // as of this exact moment.
-  void EstimateQuality();
-
-  // Returns the current estimate of the near-future quality of connections
-  // that use this local interface.
-  double quality() { return quality_; }
-
   // Debugging description of this network
   std::string ToString() const;
 
@@ -132,39 +175,10 @@
   double uniform_denominator_;
   double exponential_numerator_;
   double exponential_denominator_;
-  uint32 last_data_time_;
-  double quality_;
-
-  // Updates the statistics maintained to include the given estimate.
-  void AddDataPoint(uint32 time, double quality);
-
-  // Converts the internal state to and from a string.  This is used to record
-  // quality information into a permanent store.
-  void SetState(const std::string& str);
-  std::string GetState() const;
 
   friend class NetworkManager;
 };
 
-// Represents a session that is in progress using a particular network and can
-// provide data about the quality of the network at any given moment.
-class NetworkSession {
- public:
-  virtual ~NetworkSession() { }
-
-  // Determines whether this session has an estimate at this moment.  We will
-  // only call GetCurrentQuality when this returns true.
-  virtual bool HasQuality() = 0;
-
-  // Returns an estimate of the quality at this exact moment.  The result should
-  // be a MOS (mean opinion score) value.
-  virtual float GetCurrentQuality() = 0;
-};
-
-const double QUALITY_BAD  = 3.0;
-const double QUALITY_FAIR = 3.35;
-const double QUALITY_GOOD = 3.7;
-
 }  // namespace talk_base
 
 #endif  // TALK_BASE_NETWORK_H_
diff --git a/talk/base/signalthread.h b/talk/base/signalthread.h
index d103acf..e7f2994 100644
--- a/talk/base/signalthread.h
+++ b/talk/base/signalthread.h
@@ -30,6 +30,7 @@
 
 #include <string>
 
+#include "talk/base/constructormagic.h"
 #include "talk/base/thread.h"
 #include "talk/base/sigslot.h"
 
@@ -117,16 +118,17 @@
     kStopping,        // Work is being interrupted
   };
 
-  friend class Worker;
   class Worker : public Thread {
    public:
     explicit Worker(SignalThread* parent) : parent_(parent) {}
     virtual void Run() { parent_->Run(); }
+
    private:
     SignalThread* parent_;
+
+    DISALLOW_IMPLICIT_CONSTRUCTORS(Worker);
   };
 
-  friend class EnterExit;
   class EnterExit {
    public:
     explicit EnterExit(SignalThread* t) : t_(t) {
@@ -142,8 +144,11 @@
       if (d)
         delete t_;
     }
+
    private:
     SignalThread* t_;
+
+    DISALLOW_IMPLICIT_CONSTRUCTORS(EnterExit);
   };
 
   void Run();
@@ -154,6 +159,8 @@
   CriticalSection cs_;
   State state_;
   int refcount_;
+
+  DISALLOW_COPY_AND_ASSIGN(SignalThread);
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/talk/base/stringencode.cc b/talk/base/stringencode.cc
index 81ab6e9..49dc42f 100644
--- a/talk/base/stringencode.cc
+++ b/talk/base/stringencode.cc
@@ -530,6 +530,44 @@
   return fields->size();
 }
 
+size_t tokenize_append(const std::string& source, char delimiter,
+                       std::vector<std::string>* fields) {
+  if (!fields) return 0;
+
+  std::vector<std::string> new_fields;
+  tokenize(source, delimiter, &new_fields);
+  fields->insert(fields->end(), new_fields.begin(), new_fields.end());
+  return fields->size();
+}
+
+size_t tokenize(const std::string& source, char delimiter, char start_mark,
+                char end_mark, std::vector<std::string>* fields) {
+  if (!fields) return 0;
+  fields->clear();
+
+  std::string remain_source = source;
+  while (!remain_source.empty()) {
+    size_t start_pos = remain_source.find(start_mark);
+    if (std::string::npos == start_pos) break;
+    std::string pre_mark;
+    if (start_pos > 0) {
+      pre_mark = remain_source.substr(0, start_pos - 1);
+    }
+
+    ++start_pos;
+    size_t end_pos = remain_source.find(end_mark, start_pos);
+    if (std::string::npos == end_pos) break;
+
+    // We have found the matching marks. First tokenize the pre-mask. Then add
+    // the marked part as a single field. Finally, loop back for the post-mark.
+    tokenize_append(pre_mark, delimiter, fields);
+    fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
+    remain_source = remain_source.substr(end_pos + 1);
+  }
+
+  return tokenize_append(remain_source, delimiter, fields);
+}
+
 size_t split(const std::string& source, char delimiter,
              std::vector<std::string>* fields) {
   ASSERT(NULL != fields);
diff --git a/talk/base/stringencode.h b/talk/base/stringencode.h
index a44b4e1..92c794b 100644
--- a/talk/base/stringencode.h
+++ b/talk/base/stringencode.h
@@ -135,6 +135,20 @@
 size_t tokenize(const std::string& source, char delimiter,
                 std::vector<std::string>* fields);
 
+// Tokenize and append the tokens to fields. Return the new size of fields.
+size_t tokenize_append(const std::string& source, char delimiter,
+                       std::vector<std::string>* fields);
+
+// Splits the source string into multiple fields separated by delimiter, with
+// duplicates of delimiter ignored. Trailing delimiter ignored. A substring in
+// between the start_mark and the end_mark is treated as a single field. Return
+// the size of fields. For example, if source is "filename
+// \"/Library/Application Support/media content.txt\"", delimiter is ' ', and
+// the start_mark and end_mark are '"', this method returns two fields:
+// "filename" and "/Library/Application Support/media content.txt".
+size_t tokenize(const std::string& source, char delimiter, char start_mark,
+                char end_mark, std::vector<std::string>* fields);
+
 // Safe sprintf to std::string
 //void sprintf(std::string& value, size_t maxlen, const char * format, ...)
 //     PRINTF_FORMAT(3);
diff --git a/talk/base/thread.h b/talk/base/thread.h
index 42656e9..efbdae7 100644
--- a/talk/base/thread.h
+++ b/talk/base/thread.h
@@ -37,6 +37,7 @@
 #include <pthread.h>
 #endif
 
+#include "talk/base/constructormagic.h"
 #include "talk/base/messagequeue.h"
 
 #ifdef WIN32
@@ -87,6 +88,8 @@
 #ifdef WIN32
   static DWORD key_;
 #endif
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadManager);
 };
 
 class Thread;
@@ -109,6 +112,12 @@
  public:
   virtual ~Runnable() {}
   virtual void Run(Thread* thread) = 0;
+
+ protected:
+  Runnable() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Runnable);
 };
 
 class Thread : public MessageQueue {
@@ -208,6 +217,8 @@
   bool owned_;
 
   friend class ThreadManager;
+
+  DISALLOW_COPY_AND_ASSIGN(Thread);
 };
 
 // AutoThread automatically installs itself at construction
@@ -215,16 +226,25 @@
 // _not already_ associated with the current OS thread.
 
 class AutoThread : public Thread {
-public:
+ public:
   AutoThread(SocketServer* ss = 0);
   virtual ~AutoThread();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AutoThread);
 };
 
 // Win32 extension for threads that need to use COM
 #ifdef WIN32
 class ComThread : public Thread {
+ public:
+  ComThread() {}
+
  protected:
   virtual void Run();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ComThread);
 };
 #endif
 
@@ -238,8 +258,11 @@
   ~SocketServerScope() {
     Thread::Current()->set_socketserver(old_ss_);
   }
+
  private:
   SocketServer* old_ss_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SocketServerScope);
 };
 
 }  // namespace talk_base
diff --git a/talk/examples/call/call_main.cc b/talk/examples/call/call_main.cc
index b6ebb3a..0956836 100644
--- a/talk/examples/call/call_main.cc
+++ b/talk/examples/call/call_main.cc
@@ -46,6 +46,7 @@
 #include "talk/examples/login/xmpppump.h"
 #include "talk/examples/call/callclient.h"
 #include "talk/examples/call/console.h"
+#include "talk/session/phone/fakemediaengine.h"
 #include "talk/session/phone/filemediaengine.h"
 #include "talk/session/phone/mediasessionclient.h"
 #include "talk/session/phone/srtpfilter.h"
@@ -216,6 +217,7 @@
   return file_media_engine;
 }
 
+
 // TODO: Move this into Console.
 void Print(const char* chars) {
   printf("%s", chars);
@@ -247,6 +249,7 @@
   DEFINE_string(voiceinput, NULL, "RTP dump file for voice input.");
   DEFINE_string(voiceoutput, NULL, "RTP dump file for voice output.");
   DEFINE_string(videoinput, NULL, "RTP dump file for video input.");
+  DEFINE_string(yuvvideoinput, NULL, "YUV file for video input.");
   DEFINE_string(videooutput, NULL, "RTP dump file for video output.");
   DEFINE_bool(debugsrtp, false, "Enable debugging for srtp.");
   DEFINE_bool(help, false, "Prints this message");
@@ -389,7 +392,6 @@
     // The engine will be released by the client later.
     client->SetMediaEngine(engine);
   }
-
   Console *console = new Console(main_thread, client);
   client->SetConsole(console);
   client->SetAutoAccept(auto_accept);
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 3fbd579..943ccb5 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -329,7 +329,7 @@
   // TODO: It looks like we are leaking many objects. E.g.
   // |network_manager_| is never deleted.
 
-  network_manager_ = new talk_base::NetworkManager();
+  network_manager_ = new talk_base::BasicNetworkManager();
 
   // TODO: Decide if the relay address should be specified here.
   talk_base::SocketAddress stun_addr("stun.l.google.com", 19302);
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 5bfeb87..3af2b39 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -66,11 +66,11 @@
 class PortAllocator;
 class MediaEngine;
 class MediaSessionClient;
-class NamedSource;
 class Receiver;
 class Call;
-struct CallOptions;
 class SessionManagerTask;
+struct CallOptions;
+struct NamedSource;
 }
 
 struct RosterItem {
diff --git a/talk/main.scons b/talk/main.scons
index c8b0fd9..9289dcf 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -79,11 +79,11 @@
 
 # List all the locales we localize to.
 root_env.AppendUnique(locales = [
-    'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en-GB', 'es', 'es-419',
-    'et', 'fa', 'fi', 'fil', 'fr', 'gu', 'hi', 'hr', 'hu', 'id', 'is', 'it',
-    'iw', 'ja', 'kn', 'ko', 'lt', 'lv', 'ml', 'mr', 'ms', 'nl', 'no', 'or',
-    'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'ta', 'te',
-    'th', 'tl', 'tr', 'uk', 'ur', 'vi', 'zh-CN', 'zh-TW'])
+    'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en', 'en-GB', 'es',
+    'es-419', 'et', 'fa', 'fi', 'fil', 'fr', 'gu', 'hi', 'hr', 'hu', 'id',
+    'is', 'it', 'iw', 'ja', 'kn', 'ko', 'lt', 'lv', 'ml', 'mr', 'ms', 'nl',
+    'no', 'or', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv',
+    'ta', 'te', 'th', 'tl', 'tr', 'uk', 'ur', 'vi', 'zh-CN', 'zh-TW'])
 
 #-------------------------------------------------------------------------------
 # W I N D O W S
@@ -117,6 +117,7 @@
     '/Zc:forScope', # handle 'for (int i = 0 ...)' right
     '/EHs-c-',      # disable C++ EH
     '/GR-',         # disable RTTI
+    '/Gy',          # enable function level linking
     '/wd4996',      # ignore POSIX deprecated warnings
 
     # promote certain level 4 warnings
@@ -169,6 +170,7 @@
     '$PLATFORM_SDK_VISTA_6_0_DIR/Lib'
   ],
   LINKFLAGS = [
+    '-opt:ref', # Remove unused references (functions/data).
     '-manifest' # TODO: Why do we need this?
   ],
   MIDLFLAGS = [
@@ -247,7 +249,6 @@
 
 envs.append(win_opt_env)
 
-
 #-------------------------------------------------------------------------------
 # P O S I X
 #
@@ -267,6 +268,9 @@
     '-Werror',
     '-Wno-switch',
     '-fno-exceptions',
+    # Needed for a clean ABI and for link-time dead-code removal to work
+    # properly.
+    '-fvisibility=hidden',
   ],
   CXXFLAGS = [
     '-Wno-non-virtual-dtor',
@@ -305,6 +309,8 @@
     '-ObjC',
     '-arch', 'i386',
     '-m32',
+    # Enable dead-code removal.
+    '-dead_strip',
   ],
   FRAMEWORKS = [
     'CoreServices',
@@ -365,13 +371,9 @@
     'LINUX',
   ],
   CCFLAGS = [
-    # TODO: Some or all of this may be desirable for Mac too.
     # Needed for link-time dead-code removal to work properly.
     '-ffunction-sections',
     '-fdata-sections',
-    # Needed for a clean ABI and for link-time dead-code removal to work
-    # properly.
-    '-fvisibility=hidden',
     # Generate debugging info in the DWARF2 format.
     '-gdwarf-2',
     # Generate maximal debugging information. (It is stripped from what we ship
diff --git a/talk/p2p/client/basicportallocator.cc b/talk/p2p/client/basicportallocator.cc
index 6c54c09..51d069e 100644
--- a/talk/p2p/client/basicportallocator.cc
+++ b/talk/p2p/client/basicportallocator.cc
@@ -232,7 +232,11 @@
     : PortAllocatorSession(allocator->flags()), allocator_(allocator),
       name_(name), session_type_(session_type), network_thread_(NULL),
       socket_factory_(allocator->socket_factory()), allocation_started_(false),
+      network_manager_started_(false),
       running_(false) {
+  allocator_->network_manager()->SignalNetworksChanged.connect(
+      this, &BasicPortAllocatorSession::OnNetworksChanged);
+  allocator_->network_manager()->StartUpdating();
 }
 
 BasicPortAllocatorSession::~BasicPortAllocatorSession() {
@@ -347,14 +351,21 @@
   network_thread_->Post(this, MSG_ALLOCATE);
 }
 
+void BasicPortAllocatorSession::OnAllocate() {
+  if (network_manager_started_)
+    DoAllocate();
+
+  allocation_started_ = true;
+  if (running_)
+    network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+}
+
 // For each network, see if we have a sequence that covers it already.  If not,
 // create a new sequence to create the appropriate ports.
-void BasicPortAllocatorSession::OnAllocate() {
+void BasicPortAllocatorSession::DoAllocate() {
   std::vector<talk_base::Network*> networks;
-
-  if (!allocator_->network_manager()->GetNetworks(&networks)) {
-    LOG(LS_ERROR) << "Failed to enumerate networks";
-  } else if (networks.empty()) {
+  allocator_->network_manager()->GetNetworks(&networks);
+  if (networks.empty()) {
     LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated";
   } else {
     for (uint32 i = 0; i < networks.size(); ++i) {
@@ -374,8 +385,8 @@
         sequence_flags |= PORTALLOCATOR_DISABLE_RELAY;
       }
 
-      // Disable phases that would only create ports equivalent to ones that we
-      // have already made.
+      // Disable phases that would only create ports equivalent to
+      // ones that we have already made.
       DisableEquivalentPhases(networks[i], config, &sequence_flags);
 
       if ((sequence_flags & DISABLE_ALL_PHASES) == DISABLE_ALL_PHASES) {
@@ -391,10 +402,12 @@
       sequences_.push_back(sequence);
     }
   }
+}
 
-  allocation_started_ = true;
-  if (running_)
-    network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+void BasicPortAllocatorSession::OnNetworksChanged() {
+  network_manager_started_ = true;
+  if (allocation_started_)
+    DoAllocate();
 }
 
 void BasicPortAllocatorSession::DisableEquivalentPhases(
diff --git a/talk/p2p/client/basicportallocator.h b/talk/p2p/client/basicportallocator.h
index c9adfd8..0e1d00a 100644
--- a/talk/p2p/client/basicportallocator.h
+++ b/talk/p2p/client/basicportallocator.h
@@ -140,6 +140,8 @@
   void OnConfigTimeout();
   void AllocatePorts();
   void OnAllocate();
+  void DoAllocate();
+  void OnNetworksChanged();
   void DisableEquivalentPhases(talk_base::Network* network,
       PortConfiguration* config, uint32* flags);
   void AddAllocatedPort(Port* port, AllocationSequence* seq, float pref,
@@ -159,6 +161,7 @@
   talk_base::PacketSocketFactory* socket_factory_;
   bool configuration_done_;
   bool allocation_started_;
+  bool network_manager_started_;
   bool running_;  // set when StartGetAllPorts is called
   std::vector<PortConfiguration*> configs_;
   std::vector<AllocationSequence*> sequences_;
diff --git a/talk/p2p/client/socketmonitor.cc b/talk/p2p/client/socketmonitor.cc
index bf32d84..5fe5304 100644
--- a/talk/p2p/client/socketmonitor.cc
+++ b/talk/p2p/client/socketmonitor.cc
@@ -138,7 +138,6 @@
       info.recv_bytes_second = connection->recv_bytes_second();
       info.local_candidate = connection->local_candidate();
       info.remote_candidate = connection->remote_candidate();
-      info.est_quality = connection->port()->network()->quality();
       info.key = connection;
       connection_infos_.push_back(info);
     }
diff --git a/talk/p2p/client/socketmonitor.h b/talk/p2p/client/socketmonitor.h
index cf29d9d..9facf3f 100644
--- a/talk/p2p/client/socketmonitor.h
+++ b/talk/p2p/client/socketmonitor.h
@@ -51,7 +51,6 @@
   size_t recv_bytes_second;
   Candidate local_candidate;
   Candidate remote_candidate;
-  double est_quality;
   void *key;
 };
 
diff --git a/talk/session/phone/currentspeakermonitor.h b/talk/session/phone/currentspeakermonitor.h
index adf6b48..84207fb 100644
--- a/talk/session/phone/currentspeakermonitor.h
+++ b/talk/session/phone/currentspeakermonitor.h
@@ -38,11 +38,11 @@
 
 namespace cricket {
 
-class AudioInfo;
 class BaseSession;
 class Call;
-class MediaSources;
 class Session;
+struct AudioInfo;
+struct MediaSources;
 
 // Note that the call's audio monitor must be started before this is started.
 // It's recommended that the audio monitor be started with a 100 ms period.
diff --git a/talk/session/phone/videorendererfactory.h b/talk/session/phone/videorendererfactory.h
index 14e9a3a..058bf8c 100644
--- a/talk/session/phone/videorendererfactory.h
+++ b/talk/session/phone/videorendererfactory.h
@@ -62,4 +62,4 @@
 
 }  // namespace cricket
 
-#endif  // TALK_SESSION_PHONE_VIDEORENDERERFACTORY_H_
\ No newline at end of file
+#endif  // TALK_SESSION_PHONE_VIDEORENDERERFACTORY_H_