michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "NetworkActivityMonitor.h" michael@0: #include "prmem.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsPISocketTransportService.h" michael@0: #include "nsSocketTransportService2.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/Services.h" michael@0: #include "prerror.h" michael@0: michael@0: using namespace mozilla::net; michael@0: michael@0: static PRStatus michael@0: nsNetMon_Connect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: PRStatus ret; michael@0: PRErrorCode code; michael@0: ret = fd->lower->methods->connect(fd->lower, addr, timeout); michael@0: if (ret == PR_SUCCESS || (code = PR_GetError()) == PR_WOULD_BLOCK_ERROR || michael@0: code == PR_IN_PROGRESS_ERROR) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_Read(PRFileDesc *fd, void *buf, int32_t len) michael@0: { michael@0: int32_t ret; michael@0: ret = fd->lower->methods->read(fd->lower, buf, len); michael@0: if (ret >= 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_Write(PRFileDesc *fd, const void *buf, int32_t len) michael@0: { michael@0: int32_t ret; michael@0: ret = fd->lower->methods->write(fd->lower, buf, len); michael@0: if (ret > 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_Writev(PRFileDesc *fd, michael@0: const PRIOVec *iov, michael@0: int32_t size, michael@0: PRIntervalTime timeout) michael@0: { michael@0: int32_t ret; michael@0: ret = fd->lower->methods->writev(fd->lower, iov, size, timeout); michael@0: if (ret > 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_Recv(PRFileDesc *fd, michael@0: void *buf, michael@0: int32_t amount, michael@0: int flags, michael@0: PRIntervalTime timeout) michael@0: { michael@0: int32_t ret; michael@0: ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout); michael@0: if (ret >= 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_Send(PRFileDesc *fd, michael@0: const void *buf, michael@0: int32_t amount, michael@0: int flags, michael@0: PRIntervalTime timeout) michael@0: { michael@0: int32_t ret; michael@0: ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout); michael@0: if (ret > 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_RecvFrom(PRFileDesc *fd, michael@0: void *buf, michael@0: int32_t amount, michael@0: int flags, michael@0: PRNetAddr *addr, michael@0: PRIntervalTime timeout) michael@0: { michael@0: int32_t ret; michael@0: ret = fd->lower->methods->recvfrom(fd->lower, michael@0: buf, michael@0: amount, michael@0: flags, michael@0: addr, michael@0: timeout); michael@0: if (ret >= 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_SendTo(PRFileDesc *fd, michael@0: const void *buf, michael@0: int32_t amount, michael@0: int flags, michael@0: const PRNetAddr *addr, michael@0: PRIntervalTime timeout) michael@0: { michael@0: int32_t ret; michael@0: ret = fd->lower->methods->sendto(fd->lower, michael@0: buf, michael@0: amount, michael@0: flags, michael@0: addr, michael@0: timeout); michael@0: if (ret > 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); michael@0: return ret; michael@0: } michael@0: michael@0: static int32_t michael@0: nsNetMon_AcceptRead(PRFileDesc *listenSock, michael@0: PRFileDesc **acceptedSock, michael@0: PRNetAddr **peerAddr, michael@0: void *buf, michael@0: int32_t amount, michael@0: PRIntervalTime timeout) michael@0: { michael@0: int32_t ret; michael@0: ret = listenSock->lower->methods->acceptread(listenSock->lower, michael@0: acceptedSock, michael@0: peerAddr, michael@0: buf, michael@0: amount, michael@0: timeout); michael@0: if (ret > 0) michael@0: NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); michael@0: return ret; michael@0: } michael@0: michael@0: michael@0: class NotifyNetworkActivity : public nsRunnable { michael@0: public: michael@0: NotifyNetworkActivity(NetworkActivityMonitor::Direction aDirection) michael@0: : mDirection(aDirection) michael@0: {} michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (!obs) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: obs->NotifyObservers(nullptr, michael@0: mDirection == NetworkActivityMonitor::kUpload michael@0: ? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC michael@0: : NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, michael@0: nullptr); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsCOMPtr mObs; michael@0: NetworkActivityMonitor::Direction mDirection; michael@0: }; michael@0: michael@0: NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr; michael@0: michael@0: NetworkActivityMonitor::NetworkActivityMonitor() michael@0: : mLayerIdentity(PR_INVALID_IO_LAYER) michael@0: , mBlipInterval(PR_INTERVAL_NO_TIMEOUT) michael@0: { michael@0: MOZ_COUNT_CTOR(NetworkActivityMonitor); michael@0: michael@0: NS_ASSERTION(gInstance==nullptr, michael@0: "multiple NetworkActivityMonitor instances!"); michael@0: } michael@0: michael@0: NetworkActivityMonitor::~NetworkActivityMonitor() michael@0: { michael@0: MOZ_COUNT_DTOR(NetworkActivityMonitor); michael@0: gInstance = nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: NetworkActivityMonitor::Init(int32_t blipInterval) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (gInstance) michael@0: return NS_ERROR_ALREADY_INITIALIZED; michael@0: michael@0: NetworkActivityMonitor * mon = new NetworkActivityMonitor(); michael@0: rv = mon->Init_Internal(blipInterval); michael@0: if (NS_FAILED(rv)) { michael@0: delete mon; michael@0: return rv; michael@0: } michael@0: michael@0: gInstance = mon; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NetworkActivityMonitor::Shutdown() michael@0: { michael@0: if (!gInstance) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: delete gInstance; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NetworkActivityMonitor::Init_Internal(int32_t blipInterval) michael@0: { michael@0: mLayerIdentity = PR_GetUniqueIdentity("network activity monitor layer"); michael@0: mLayerMethods = *PR_GetDefaultIOMethods(); michael@0: mLayerMethods.connect = nsNetMon_Connect; michael@0: mLayerMethods.read = nsNetMon_Read; michael@0: mLayerMethods.write = nsNetMon_Write; michael@0: mLayerMethods.writev = nsNetMon_Writev; michael@0: mLayerMethods.recv = nsNetMon_Recv; michael@0: mLayerMethods.send = nsNetMon_Send; michael@0: mLayerMethods.recvfrom = nsNetMon_RecvFrom; michael@0: mLayerMethods.sendto = nsNetMon_SendTo; michael@0: mLayerMethods.acceptread = nsNetMon_AcceptRead; michael@0: michael@0: mBlipInterval = PR_MillisecondsToInterval(blipInterval); michael@0: // Set the last notification times to time that has just expired, so any michael@0: // activity even right now will trigger notification. michael@0: mLastNotificationTime[kUpload] = PR_IntervalNow() - mBlipInterval; michael@0: mLastNotificationTime[kDownload] = mLastNotificationTime[kUpload]; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NetworkActivityMonitor::AttachIOLayer(PRFileDesc *fd) michael@0: { michael@0: if (!gInstance) michael@0: return NS_OK; michael@0: michael@0: PRFileDesc * layer; michael@0: PRStatus status; michael@0: michael@0: layer = PR_CreateIOLayerStub(gInstance->mLayerIdentity, michael@0: &gInstance->mLayerMethods); michael@0: if (!layer) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer); michael@0: michael@0: if (status == PR_FAILURE) { michael@0: PR_DELETE(layer); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NetworkActivityMonitor::DataInOut(Direction direction) michael@0: { michael@0: NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); michael@0: michael@0: if (gInstance) { michael@0: PRIntervalTime now = PR_IntervalNow(); michael@0: if ((now - gInstance->mLastNotificationTime[direction]) > michael@0: gInstance->mBlipInterval) { michael@0: gInstance->mLastNotificationTime[direction] = now; michael@0: gInstance->PostNotification(direction); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: NetworkActivityMonitor::PostNotification(Direction direction) michael@0: { michael@0: nsRefPtr ev = new NotifyNetworkActivity(direction); michael@0: NS_DispatchToMainThread(ev); michael@0: }