summaryrefslogtreecommitdiffstats
path: root/netwerk/base/NetworkActivityMonitor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/base/NetworkActivityMonitor.cpp')
-rw-r--r--netwerk/base/NetworkActivityMonitor.cpp300
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);
+}