diff options
Diffstat (limited to 'netwerk/base/NetworkActivityMonitor.cpp')
-rw-r--r-- | netwerk/base/NetworkActivityMonitor.cpp | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/netwerk/base/NetworkActivityMonitor.cpp b/netwerk/base/NetworkActivityMonitor.cpp new file mode 100644 index 000000000..887878977 --- /dev/null +++ b/netwerk/base/NetworkActivityMonitor.cpp @@ -0,0 +1,300 @@ +/* -*- 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 mozilla::Runnable { +public: + explicit NotifyNetworkActivity(NetworkActivityMonitor::Direction aDirection) + : mDirection(aDirection) + {} + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (!obs) + return NS_ERROR_FAILURE; + + obs->NotifyObservers(nullptr, + mDirection == NetworkActivityMonitor::kUpload + ? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC + : NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, + nullptr); + return NS_OK; + } +private: + NetworkActivityMonitor::Direction mDirection; +}; + +NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr; +static PRDescIdentity sNetActivityMonitorLayerIdentity; +static PRIOMethods sNetActivityMonitorLayerMethods; +static PRIOMethods *sNetActivityMonitorLayerMethodsPtr = nullptr; + +NetworkActivityMonitor::NetworkActivityMonitor() + : mBlipInterval(PR_INTERVAL_NO_TIMEOUT) +{ + MOZ_COUNT_CTOR(NetworkActivityMonitor); + + NS_ASSERTION(gInstance==nullptr, + "multiple NetworkActivityMonitor instances!"); +} + +NetworkActivityMonitor::~NetworkActivityMonitor() +{ + MOZ_COUNT_DTOR(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) +{ + if (!sNetActivityMonitorLayerMethodsPtr) { + sNetActivityMonitorLayerIdentity = + PR_GetUniqueIdentity("network activity monitor layer"); + sNetActivityMonitorLayerMethods = *PR_GetDefaultIOMethods(); + sNetActivityMonitorLayerMethods.connect = nsNetMon_Connect; + sNetActivityMonitorLayerMethods.read = nsNetMon_Read; + sNetActivityMonitorLayerMethods.write = nsNetMon_Write; + sNetActivityMonitorLayerMethods.writev = nsNetMon_Writev; + sNetActivityMonitorLayerMethods.recv = nsNetMon_Recv; + sNetActivityMonitorLayerMethods.send = nsNetMon_Send; + sNetActivityMonitorLayerMethods.recvfrom = nsNetMon_RecvFrom; + sNetActivityMonitorLayerMethods.sendto = nsNetMon_SendTo; + sNetActivityMonitorLayerMethods.acceptread = nsNetMon_AcceptRead; + sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods; + } + + 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(sNetActivityMonitorLayerIdentity, + sNetActivityMonitorLayerMethodsPtr); + 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) +{ + nsCOMPtr<nsIRunnable> ev = new NotifyNetworkActivity(direction); + NS_DispatchToMainThread(ev); +} |