/* -*- 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 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 ev = new NotifyNetworkActivity(direction); NS_DispatchToMainThread(ev); }