From 874dc9c6a29d765b5b7f117d646f9cf57a9c7bd0 Mon Sep 17 00:00:00 2001 From: Michal Novotny Date: Fri, 28 Sep 2012 13:53:52 +0200 Subject: [PATCH] Bug 777445 - Network activity indicator for B2G, r=jduell --- b2g/app/b2g.js | 6 + modules/libpref/src/init/all.js | 6 + .../public/nsPISocketTransportService.idl | 13 + netwerk/base/src/Makefile.in | 1 + netwerk/base/src/NetworkActivityMonitor.cpp | 294 ++++++++++++++++++ netwerk/base/src/NetworkActivityMonitor.h | 49 +++ netwerk/base/src/nsSocketTransport2.cpp | 4 + .../base/src/nsSocketTransportService2.cpp | 16 +- 8 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 netwerk/base/src/NetworkActivityMonitor.cpp create mode 100644 netwerk/base/src/NetworkActivityMonitor.h diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 149c6198738..abcd8bad89a 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -553,3 +553,9 @@ pref("ui.mouse.radius.enabled", true); // Disable native prompt pref("browser.prompt.allowNative", false); + +// Minimum delay in milliseconds between network activity notifications (0 means +// no notifications). The delay is the same for both download and upload, though +// they are handled separately. This pref is only read once at startup: +// a restart is required to enable a new value. +pref("network.activity.blipIntervalMilliseconds", 250); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index bb5caaefdbe..c125d44a885 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3745,3 +3745,9 @@ pref("dom.mozApps.maxLocalId", 1000); // Let us know wether we should run the permissions update algorithm. // See Bug 787439 pref("dom.mozApps.runUpdate", true); + +// Minimum delay in milliseconds between network activity notifications (0 means +// no notifications). The delay is the same for both download and upload, though +// they are handled separately. This pref is only read once at startup: +// a restart is required to enable a new value. +pref("network.activity.blipIntervalMilliseconds", 0); diff --git a/netwerk/base/public/nsPISocketTransportService.idl b/netwerk/base/public/nsPISocketTransportService.idl index c1556c5acc3..68bc56b2053 100644 --- a/netwerk/base/public/nsPISocketTransportService.idl +++ b/netwerk/base/public/nsPISocketTransportService.idl @@ -37,3 +37,16 @@ interface nsPISocketTransportService : nsISocketTransportService */ attribute boolean offline; }; + +%{C++ +/* + * Network activity indicator: we send out these topics no more than every + * blipIntervalMilliseconds (as set by the + * "network.activity.blipIntervalMilliseconds" preference: if 0 no notifications + * are sent) if the network is currently active (i.e. we're sending/receiving + * data to/from the socket). + */ +#define NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC "network-activity-blip-upload" +#define NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC "network-activity-blip-download" + +%} diff --git a/netwerk/base/src/Makefile.in b/netwerk/base/src/Makefile.in index 035510fc048..7ebd512c282 100644 --- a/netwerk/base/src/Makefile.in +++ b/netwerk/base/src/Makefile.in @@ -67,6 +67,7 @@ CPPSRCS = \ nsPreloadedStream.cpp \ nsStreamListenerWrapper.cpp \ ProxyAutoConfig.cpp \ + NetworkActivityMonitor.cpp \ $(NULL) LOCAL_INCLUDES += -I$(topsrcdir)/dom/base diff --git a/netwerk/base/src/NetworkActivityMonitor.cpp b/netwerk/base/src/NetworkActivityMonitor.cpp new file mode 100644 index 00000000000..c093e338d0f --- /dev/null +++ b/netwerk/base/src/NetworkActivityMonitor.cpp @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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 "NetworkActivityMonitor.h" +#include "prmem.h" +#include "nsIObserverService.h" +#include "nsPISocketTransportService.h" +#include "nsSocketTransportService2.h" +#include "nsThreadUtils.h" +#include "mozilla/Services.h" +#include "prerror.h" + +using namespace mozilla::net; + +static PRStatus +nsNetMon_Connect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRStatus ret; + PRErrorCode code; + ret = fd->lower->methods->connect(fd->lower, addr, timeout); + if (ret == PR_SUCCESS || (code = PR_GetError()) == PR_WOULD_BLOCK_ERROR || + code == PR_IN_PROGRESS_ERROR) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); + return ret; +} + +static int32_t +nsNetMon_Read(PRFileDesc *fd, void *buf, int32_t len) +{ + int32_t ret; + ret = fd->lower->methods->read(fd->lower, buf, len); + if (ret >= 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); + return ret; +} + +static int32_t +nsNetMon_Write(PRFileDesc *fd, const void *buf, int32_t len) +{ + int32_t ret; + ret = fd->lower->methods->write(fd->lower, buf, len); + if (ret > 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); + return ret; +} + +static int32_t +nsNetMon_Writev(PRFileDesc *fd, + const PRIOVec *iov, + int32_t size, + PRIntervalTime timeout) +{ + int32_t ret; + ret = fd->lower->methods->writev(fd->lower, iov, size, timeout); + if (ret > 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); + return ret; +} + +static int32_t +nsNetMon_Recv(PRFileDesc *fd, + void *buf, + int32_t amount, + int flags, + PRIntervalTime timeout) +{ + int32_t ret; + ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout); + if (ret >= 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); + return ret; +} + +static int32_t +nsNetMon_Send(PRFileDesc *fd, + const void *buf, + int32_t amount, + int flags, + PRIntervalTime timeout) +{ + int32_t ret; + ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout); + if (ret > 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); + return ret; +} + +static int32_t +nsNetMon_RecvFrom(PRFileDesc *fd, + void *buf, + int32_t amount, + int flags, + PRNetAddr *addr, + PRIntervalTime timeout) +{ + int32_t ret; + ret = fd->lower->methods->recvfrom(fd->lower, + buf, + amount, + flags, + addr, + timeout); + if (ret >= 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); + return ret; +} + +static int32_t +nsNetMon_SendTo(PRFileDesc *fd, + const void *buf, + int32_t amount, + int flags, + const PRNetAddr *addr, + PRIntervalTime timeout) +{ + int32_t ret; + ret = fd->lower->methods->sendto(fd->lower, + buf, + amount, + flags, + addr, + timeout); + if (ret > 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); + return ret; +} + +static int32_t +nsNetMon_AcceptRead(PRFileDesc *listenSock, + PRFileDesc **acceptedSock, + PRNetAddr **peerAddr, + void *buf, + int32_t amount, + PRIntervalTime timeout) +{ + int32_t ret; + ret = listenSock->lower->methods->acceptread(listenSock->lower, + acceptedSock, + peerAddr, + buf, + amount, + timeout); + if (ret > 0) + NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); + return ret; +} + + +class NotifyNetworkActivity : public nsRunnable { +public: + NotifyNetworkActivity(nsIObserverService* aObs, + NetworkActivityMonitor::Direction aDirection) + : mObs(aObs) + , mDirection(aDirection) + {} + NS_IMETHOD Run() + { + mObs->NotifyObservers(nullptr, + mDirection == NetworkActivityMonitor::kUpload + ? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC + : NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, + nullptr); + return NS_OK; + } +private: + nsCOMPtr mObs; + NetworkActivityMonitor::Direction mDirection; +}; + +NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr; + +NetworkActivityMonitor::NetworkActivityMonitor() + : mLayerIdentity(PR_INVALID_IO_LAYER) + , mBlipInterval(PR_INTERVAL_NO_TIMEOUT) + , mLastNotificationTime({PR_INTERVAL_NO_TIMEOUT, PR_INTERVAL_NO_TIMEOUT}) +{ + NS_ASSERTION(gInstance==nullptr, + "multiple NetworkActivityMonitor instances!"); +} + +NetworkActivityMonitor::~NetworkActivityMonitor() +{ + gInstance = nullptr; +} + +nsresult +NetworkActivityMonitor::Init(int32_t blipInterval) +{ + nsresult rv; + + if (gInstance) + return NS_ERROR_ALREADY_INITIALIZED; + + NetworkActivityMonitor * mon = new NetworkActivityMonitor(); + rv = mon->Init_Internal(blipInterval); + if (NS_FAILED(rv)) { + delete mon; + return rv; + } + + gInstance = mon; + return NS_OK; +} + +nsresult +NetworkActivityMonitor::Shutdown() +{ + if (!gInstance) + return NS_ERROR_NOT_INITIALIZED; + + delete gInstance; + return NS_OK; +} + +nsresult +NetworkActivityMonitor::Init_Internal(int32_t blipInterval) +{ + mLayerIdentity = PR_GetUniqueIdentity("network activity monitor layer"); + mLayerMethods = *PR_GetDefaultIOMethods(); + mLayerMethods.connect = nsNetMon_Connect; + mLayerMethods.read = nsNetMon_Read; + mLayerMethods.write = nsNetMon_Write; + mLayerMethods.writev = nsNetMon_Writev; + mLayerMethods.recv = nsNetMon_Recv; + mLayerMethods.send = nsNetMon_Send; + mLayerMethods.recvfrom = nsNetMon_RecvFrom; + mLayerMethods.sendto = nsNetMon_SendTo; + mLayerMethods.acceptread = nsNetMon_AcceptRead; + + mObserverService = mozilla::services::GetObserverService(); + if (!mObserverService) + return NS_ERROR_FAILURE; + + mBlipInterval = PR_MillisecondsToInterval(blipInterval); + // Set the last notification times to time that has just expired, so any + // activity even right now will trigger notification. + mLastNotificationTime[kUpload] = PR_IntervalNow() - mBlipInterval; + mLastNotificationTime[kDownload] = mLastNotificationTime[kUpload]; + + return NS_OK; +} + +nsresult +NetworkActivityMonitor::AttachIOLayer(PRFileDesc *fd) +{ + if (!gInstance) + return NS_OK; + + PRFileDesc * layer; + PRStatus status; + + layer = PR_CreateIOLayerStub(gInstance->mLayerIdentity, + &gInstance->mLayerMethods); + if (!layer) { + return NS_ERROR_FAILURE; + } + + status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer); + + if (status == PR_FAILURE) { + PR_DELETE(layer); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +NetworkActivityMonitor::DataInOut(Direction direction) +{ + NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); + + if (gInstance) { + PRIntervalTime now = PR_IntervalNow(); + if ((now - gInstance->mLastNotificationTime[direction]) > + gInstance->mBlipInterval) { + gInstance->mLastNotificationTime[direction] = now; + gInstance->PostNotification(direction); + } + } + + return NS_OK; +} + +void +NetworkActivityMonitor::PostNotification(Direction direction) +{ + nsRefPtr ev = new NotifyNetworkActivity(mObserverService, + direction); + NS_DispatchToMainThread(ev); +} diff --git a/netwerk/base/src/NetworkActivityMonitor.h b/netwerk/base/src/NetworkActivityMonitor.h new file mode 100644 index 00000000000..2c96f45021f --- /dev/null +++ b/netwerk/base/src/NetworkActivityMonitor.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * 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/. */ + +#ifndef NetworkActivityMonitor_h___ +#define NetworkActivityMonitor_h___ + +#include "nsCOMPtr.h" +#include "prio.h" +#include "prinrval.h" + +class nsIObserverService; + +namespace mozilla { namespace net { + +class NetworkActivityMonitor +{ +public: + enum Direction { + kUpload = 0, + kDownload = 1 + }; + + NetworkActivityMonitor(); + ~NetworkActivityMonitor(); + + static nsresult Init(int32_t blipInterval); + static nsresult Shutdown(); + + static nsresult AttachIOLayer(PRFileDesc *fd); + static nsresult DataInOut(Direction direction); + +private: + nsresult Init_Internal(int32_t blipInterval); + void PostNotification(Direction direction); + + static NetworkActivityMonitor * gInstance; + PRDescIdentity mLayerIdentity; + PRIOMethods mLayerMethods; + PRIntervalTime mBlipInterval; + PRIntervalTime mLastNotificationTime[2]; + nsCOMPtr mObserverService; +}; + +}} // namespace mozilla::net + +#endif /* NetworkActivityMonitor_h___ */ diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp index d7f518de505..066b591f5d8 100644 --- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -24,6 +24,7 @@ #include "prnetdb.h" #include "prerror.h" #include "prerr.h" +#include "NetworkActivityMonitor.h" #include "nsIServiceManager.h" #include "nsISocketProviderService.h" @@ -1095,6 +1096,9 @@ nsSocketTransport::InitiateSocket() return rv; } + // Attach network activity monitor + mozilla::net::NetworkActivityMonitor::AttachIOLayer(fd); + PRStatus status; // Make the socket non-blocking... diff --git a/netwerk/base/src/nsSocketTransportService2.cpp b/netwerk/base/src/nsSocketTransportService2.cpp index cdbf6032398..2cef35f1277 100644 --- a/netwerk/base/src/nsSocketTransportService2.cpp +++ b/netwerk/base/src/nsSocketTransportService2.cpp @@ -18,6 +18,7 @@ #include "nsIPrefBranch.h" #include "nsServiceManagerUtils.h" #include "nsIOService.h" +#include "NetworkActivityMonitor.h" // XXX: There is no good header file to put these in. :( @@ -40,6 +41,7 @@ PRThread *gSocketThread = nullptr; #define SEND_BUFFER_PREF "network.tcp.sendbuffer" #define SOCKET_LIMIT_TARGET 550U #define SOCKET_LIMIT_MIN 50U +#define BLIB_INTERVAL_PREF "network.activity.blipIntervalMilliseconds" uint32_t nsSocketTransportService::gMaxCount; PRCallOnceType nsSocketTransportService::gMaxCountInitOnce; @@ -455,8 +457,18 @@ nsSocketTransportService::Init() } nsCOMPtr tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID); - if (tmpPrefService) + if (tmpPrefService) { tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false); + + int32_t blipInterval = 0; + rv = tmpPrefService->GetIntPref(BLIB_INTERVAL_PREF, &blipInterval); + if (NS_SUCCEEDED(rv) && blipInterval > 0) { + rv = mozilla::net::NetworkActivityMonitor::Init(blipInterval); + if (NS_FAILED(rv)) { + NS_WARNING("Can't initialize NetworkActivityMonitor"); + } + } + } UpdatePrefs(); mInitialized = true; @@ -501,6 +513,8 @@ nsSocketTransportService::Shutdown() if (tmpPrefService) tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this); + mozilla::net::NetworkActivityMonitor::Shutdown(); + mInitialized = false; mShuttingDown = false;