From 176a4657e1093a8f162832797b9c4aaeb9810555 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Sun, 22 Sep 2013 23:01:10 -0400 Subject: [PATCH] bug 904170 - telemetry for daily http data consumption r=jduell --- b2g/installer/package-manifest.in | 2 + browser/installer/package-manifest.in | 3 + mobile/android/installer/package-manifest.in | 3 + netwerk/protocol/http/HttpDataUsage.js | 222 +++++++++++++++++ netwerk/protocol/http/HttpDataUsage.manifest | 3 + netwerk/protocol/http/moz.build | 6 + netwerk/protocol/http/nsHttpConnection.cpp | 33 ++- netwerk/protocol/http/nsHttpConnection.h | 7 + netwerk/protocol/http/nsHttpHandler.cpp | 248 +++++++++++++++++-- netwerk/protocol/http/nsHttpHandler.h | 25 ++ netwerk/protocol/http/nsIHttpDataUsage.idl | 27 ++ toolkit/components/telemetry/Histograms.json | 28 +++ 12 files changed, 589 insertions(+), 18 deletions(-) create mode 100644 netwerk/protocol/http/HttpDataUsage.js create mode 100644 netwerk/protocol/http/HttpDataUsage.manifest create mode 100644 netwerk/protocol/http/nsIHttpDataUsage.idl diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 39fab8b73be..0e7a2cbe821 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -378,6 +378,8 @@ @BINPATH@/components/PeerConnection.js @BINPATH@/components/PeerConnection.manifest #endif +@BINPATH@/components/HttpDataUsage.manifest +@BINPATH@/components/HttpDataUsage.js @BINPATH@/components/SiteSpecificUserAgent.js @BINPATH@/components/SiteSpecificUserAgent.manifest @BINPATH@/components/storage-Legacy.js diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 684351a1111..3a5ed1014ee 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -544,6 +544,9 @@ @BINPATH@/components/PeerConnection.manifest #endif +@BINPATH@/components/HttpDataUsage.manifest +@BINPATH@/components/HttpDataUsage.js + @BINPATH@/chrome/marionette@JAREXT@ @BINPATH@/chrome/marionette.manifest @BINPATH@/components/MarionetteComponents.manifest diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 558a24dfa8f..cafba2972ac 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -406,6 +406,9 @@ @BINPATH@/components/PeerConnection.manifest #endif +@BINPATH@/components/HttpDataUsage.manifest +@BINPATH@/components/HttpDataUsage.js + #ifdef MOZ_SERVICES_HEALTHREPORT @BINPATH@/components/HealthReportComponents.manifest @BINPATH@/components/HealthReportService.js diff --git a/netwerk/protocol/http/HttpDataUsage.js b/netwerk/protocol/http/HttpDataUsage.js new file mode 100644 index 00000000000..6237e3ae34f --- /dev/null +++ b/netwerk/protocol/http/HttpDataUsage.js @@ -0,0 +1,222 @@ +/* -*- indent-tabs-mode: nil -*- */ +/* 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/. */ + +/* HTTPDATA_* telemetry producer + every 3 minutes of idle time we update a data file and report it + once a day. this avoids adding io to the shutdown path +*/ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; +const CC = Components.Constructor; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "idleService", + "@mozilla.org/widget/idleservice;1", + "nsIIdleService"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); + +const MB = 1000000; + +var gDataUsage; +function HttpDataUsage() {} +HttpDataUsage.prototype = { + classID: Components.ID("{6d72bfca-2747-4859-887f-6f06d4ce6787}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), + contractID: "@mozilla.org/network/HttpDataUsage;1", + + _isIdleObserver: false, + _locked : false, + _do_telemetry : false, + _idle_timeout : 60 * 3, + _quanta : 86400000, // per day + + _logtime : new Date(), + _ethernetRead : 0, + _ethernetWritten : 0, + _cellRead : 0, + _cellWritten : 0, + + _dataFile : FileUtils.getFile("ProfD", ["httpDataUsage.dat"], true), + _dataUsage : Cc["@mozilla.org/network/protocol;1?name=http"] + .getService(Ci.nsIHttpProtocolHandler) + .QueryInterface(Ci.nsIHttpDataUsage), + _pipe : CC("@mozilla.org/pipe;1", "nsIPipe", "init"), + _outputStream : CC("@mozilla.org/network/file-output-stream;1", + "nsIFileOutputStream", "init"), + + setup: function setup() { + gDataUsage = this; + + var enabled = false; + try { + if (Services.prefs.getBoolPref("toolkit.telemetry.enabled")) + enabled = true; + } catch (e) { } + try { + if (Services.prefs.getBoolPref("toolkit.telemetry.enabledPreRelease")) + enabled = true; + } catch (e) { } + + // this isn't important enough to worry about getting a + // runtime telemetry config change for + if (!enabled) + return; + + if (this._dataUsage == null) + return; + + idleService.addIdleObserver(this, this._idle_timeout); + this._isIdleObserver = true; + }, + + shutdown: function shutdown() { + if (this._isIdleObserver) + idleService.removeIdleObserver(this, this._idle_timeout); + this._isIdleObserver = false; + }, + + sUpdateStats2: function sUpdateStats2(stream, result) { + gDataUsage.updateStats2(stream, result); + }, + + sGatherTelemetry2: function sGatherTelemetry2(stream, result) { + gDataUsage.gatherTelemetry2(stream, result); + }, + + readCounters: function readCounters(stream, result) { + if (Components.isSuccessCode(result)) { + let count = stream.available(); + let data = NetUtil.readInputStreamToString(stream, count); + var list = data.split(","); + if (list.length == 5) { + this._logtime = new Date(Number(list[0])); + this._ethernetRead = Number(list[1]); + this._ethernetWritten = Number(list[2]); + this._cellRead = Number(list[3]); + this._cellWritten = Number(list[4]); + } + } + + this._ethernetRead += this._dataUsage.ethernetBytesRead; + this._ethernetWritten += this._dataUsage.ethernetBytesWritten; + this._cellRead += this._dataUsage.cellBytesRead; + this._cellWritten += this._dataUsage.cellBytesWritten; + this._dataUsage.resetHttpDataUsage(); + }, + + // writeCounters also releases the lock + writeCounters: function writeCounters() { + var dataout = this._logtime.getTime().toString() + "," + + this._ethernetRead.toString() + "," + + this._ethernetWritten.toString() + "," + + this._cellRead.toString() + "," + + this._cellWritten.toString() + "\n"; + + var buffer = new this._pipe(true, false, 4096, 1, null); + buffer.outputStream.write(dataout, dataout.length); + buffer.outputStream.close(); + var fileOut = new this._outputStream(this._dataFile, -1, -1, 0); + + NetUtil.asyncCopy(buffer.inputStream, fileOut, + function (result) { gDataUsage.finishedWriting(); }); + }, + + updateStats2: function updateStats2(stream, result) { + this.readCounters(stream, result); + this.writeCounters(); + }, + + gatherTelemetry2: function gatherTelemetry2(stream, result) { + this.readCounters(stream, result); + + var now = new Date(); + var elapsed = now.getTime() - this._logtime.getTime(); + // make sure we have at least 1 day of data + if (elapsed < this._quanta) { + this.finishedWriting(); + return; + } + + var days = elapsed / this._quanta; + var eInPerQuanta = Math.floor(this._ethernetRead / days); + var eOutPerQuanta = Math.floor(this._ethernetWritten / days); + var cInPerQuanta = Math.floor(this._cellRead / days); + var cOutPerQuanta = Math.floor(this._cellWritten / days); + + var histogram; + + while (elapsed >= this._quanta) { + histogram = Services.telemetry.getHistogramById("HTTPDATA_DAILY_ETHERNET_IN"); + histogram.add(Math.round(eInPerQuanta / MB)); + histogram = Services.telemetry.getHistogramById("HTTPDATA_DAILY_ETHERNET_OUT"); + histogram.add(Math.round(eOutPerQuanta / MB)); + histogram = Services.telemetry.getHistogramById("HTTPDATA_DAILY_CELL_IN"); + histogram.add(Math.round(cInPerQuanta / MB)); + histogram = Services.telemetry.getHistogramById("HTTPDATA_DAILY_CELL_OUT"); + histogram.add(Math.round(cOutPerQuanta / MB)); + + elapsed -= this._quanta; + this._ethernetRead -= eInPerQuanta; + this._ethernetWritten -= eOutPerQuanta; + this._cellRead -= cInPerQuanta; + this._cellWritten -= cOutPerQuanta; + } + this._logtime = new Date(now.getTime() - elapsed); + + this.writeCounters(); + }, + + finishedWriting : function finishedWriting() { + this._locked = false; + if (this._do_telemetry) { + this._do_telemetry = false; + this.gatherTelemetry(); + } + }, + + updateStats: function updateStats() { + if (this._locked) + return; + this._locked = true; + + NetUtil.asyncFetch(this._dataFile, this.sUpdateStats2); + }, + + gatherTelemetry: function gatherTelemetry() { + if (this._locked) + return; // oh well, maybe next time + this._locked = true; + + NetUtil.asyncFetch(this._dataFile, this.sGatherTelemetry2); + }, + + observe: function (aSubject, aTopic, aData) { + switch (aTopic) { + case "profile-after-change": + this.setup(); + break; + case "gather-telemetry": + this._do_telemetry = true; + this.updateStats(); + break; + case "idle": + this.updateStats(); + break; + case "profile-change-net-teardown": + this.shutdown(); + break; + } + }, + +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HttpDataUsage]); diff --git a/netwerk/protocol/http/HttpDataUsage.manifest b/netwerk/protocol/http/HttpDataUsage.manifest new file mode 100644 index 00000000000..4888f3fcf5d --- /dev/null +++ b/netwerk/protocol/http/HttpDataUsage.manifest @@ -0,0 +1,3 @@ +component {6d72bfca-2747-4859-887f-6f06d4ce6787} HttpDataUsage.js +contract @mozilla.org/network/HttpDataUsage;1 {6d72bfca-2747-4859-887f-6f06d4ce6787} +category profile-after-change HttpDataUsage @mozilla.org/network/HttpDataUsage;1 diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build index c8b7fa41286..d6a587a8b4d 100644 --- a/netwerk/protocol/http/moz.build +++ b/netwerk/protocol/http/moz.build @@ -13,6 +13,7 @@ XPIDL_SOURCES += [ 'nsIHttpChannelAuthProvider.idl', 'nsIHttpChannelChild.idl', 'nsIHttpChannelInternal.idl', + 'nsIHttpDataUsage.idl', 'nsIHttpEventSink.idl', 'nsIHttpHeaderVisitor.idl', 'nsIHttpProtocolHandler.idl', @@ -81,6 +82,11 @@ EXTRA_JS_MODULES += [ 'UserAgentUpdates.jsm', ] +EXTRA_COMPONENTS += [ + 'HttpDataUsage.js', + 'HttpDataUsage.manifest', +] + FAIL_ON_WARNINGS = True LIBXUL_LIBRARY = True diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index 20226232ece..9e6e55f0612 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -46,6 +46,8 @@ nsHttpConnection::nsHttpConnection() , mMaxBytesRead(0) , mTotalBytesRead(0) , mTotalBytesWritten(0) + , mUnreportedBytesRead(0) + , mUnreportedBytesWritten(0) , mKeepAlive(true) // assume to keep-alive by default , mKeepAliveMask(true) , mDontReuse(false) @@ -75,6 +77,7 @@ nsHttpConnection::~nsHttpConnection() { LOG(("Destroying nsHttpConnection @%x\n", this)); + ReportDataUsage(false); if (!mEverUsedSpdy) { LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n", this, mHttp1xTransactionCount)); @@ -1182,6 +1185,8 @@ nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason) mCallbacks = nullptr; } + ReportDataUsage(false); + if (NS_FAILED(reason)) Close(reason); @@ -1224,8 +1229,11 @@ nsHttpConnection::OnReadSegment(const char *buf, else { mLastWriteTime = PR_IntervalNow(); mSocketOutCondition = NS_OK; // reset condition - if (!mProxyConnectInProgress) + if (!mProxyConnectInProgress) { mTotalBytesWritten += *countRead; + mUnreportedBytesWritten += *countRead; + ReportDataUsage(true); + } } return mSocketOutCondition; @@ -1443,6 +1451,8 @@ nsHttpConnection::OnSocketReadable() else { mCurrentBytesRead += n; mTotalBytesRead += n; + mUnreportedBytesRead += n; + ReportDataUsage(true); if (NS_FAILED(mSocketInCondition)) { // continue waiting for the socket if necessary... if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK) @@ -1507,6 +1517,27 @@ nsHttpConnection::SetupProxyConnect() return NS_NewCStringInputStream(getter_AddRefs(mProxyConnectStream), buf); } +void +nsHttpConnection::ReportDataUsage(bool allowDefer) +{ + static const uint64_t kDeferThreshold = 128000; + + if (!mUnreportedBytesRead && !mUnreportedBytesWritten) + return; + + if (!gHttpHandler->IsTelemetryEnabled()) + return; + + if (allowDefer && + (mUnreportedBytesRead + mUnreportedBytesWritten) < kDeferThreshold) { + return; + } + + gHttpHandler->UpdateDataUsage(mCallbacks, + mUnreportedBytesRead, mUnreportedBytesWritten); + mUnreportedBytesRead = mUnreportedBytesWritten = 0; +} + //----------------------------------------------------------------------------- // nsHttpConnection::nsISupports //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h index d64a75d8cb3..4e0f9733e3e 100644 --- a/netwerk/protocol/http/nsHttpConnection.h +++ b/netwerk/protocol/http/nsHttpConnection.h @@ -193,6 +193,9 @@ private: // Directly Add a transaction to an active connection for SPDY nsresult AddTransaction(nsAHttpTransaction *, int32_t); + // used to inform nsIHttpDataUsage of transfer + void ReportDataUsage(bool); + private: nsCOMPtr mSocketTransport; nsCOMPtr mSocketIn; @@ -226,6 +229,10 @@ private: int64_t mTotalBytesRead; // total data read int64_t mTotalBytesWritten; // does not include CONNECT tunnel + // for nsIHttpDataUsage + uint64_t mUnreportedBytesRead; // subset of totalBytesRead + uint64_t mUnreportedBytesWritten; // subset of totalBytesWritten + nsRefPtr mInputOverflow; PRIntervalTime mRtt; diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 72e97ae324a..dc1dad8367a 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -69,6 +69,10 @@ #include #endif +#if defined(MOZ_WIDGET_GONK) +#include "nsINetworkManager.h" +#endif + //----------------------------------------------------------------------------- using namespace mozilla; using namespace mozilla::net; @@ -198,6 +202,12 @@ nsHttpHandler::nsHttpHandler() , mRequestTokenBucketHz(100) , mRequestTokenBucketBurst(32) , mCritialRequestPrioritization(true) + , mEthernetBytesRead(0) + , mEthernetBytesWritten(0) + , mCellBytesRead(0) + , mCellBytesWritten(0) + , mNetworkTypeKnown(false) + , mNetworkTypeWasEthernet(true) { #if defined(PR_LOGGING) gHttpLog = PR_NewLogModule("nsHttp"); @@ -1505,13 +1515,14 @@ nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings) // nsHttpHandler::nsISupports //----------------------------------------------------------------------------- -NS_IMPL_ISUPPORTS6(nsHttpHandler, +NS_IMPL_ISUPPORTS7(nsHttpHandler, nsIHttpProtocolHandler, nsIProxiedProtocolHandler, nsIProtocolHandler, nsIObserver, nsISupportsWeakReference, - nsISpeculativeConnect) + nsISpeculativeConnect, + nsIHttpDataUsage) //----------------------------------------------------------------------------- // nsHttpHandler::nsIProtocolHandler @@ -1931,45 +1942,248 @@ nsHttpHandler::SpeculativeConnect(nsIURI *aURI, return SpeculativeConnect(ci, aCallbacks); } -void -nsHttpHandler::TickleWifi(nsIInterfaceRequestor *cb) +// nsIHttpDataUsage + +NS_IMETHODIMP +nsHttpHandler::GetEthernetBytesRead(uint64_t *aEthernetBytesRead) { - if (!cb || !mWifiTickler) + MOZ_ASSERT(NS_IsMainThread()); + *aEthernetBytesRead = mEthernetBytesRead; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetEthernetBytesWritten(uint64_t *aEthernetBytesWritten) +{ + MOZ_ASSERT(NS_IsMainThread()); + *aEthernetBytesWritten = mEthernetBytesWritten; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetCellBytesRead(uint64_t *aCellBytesRead) +{ + MOZ_ASSERT(NS_IsMainThread()); + *aCellBytesRead = mCellBytesRead; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetCellBytesWritten(uint64_t *aCellBytesWritten) +{ + MOZ_ASSERT(NS_IsMainThread()); + *aCellBytesWritten = mCellBytesWritten; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::ResetHttpDataUsage() +{ + MOZ_ASSERT(NS_IsMainThread()); + mEthernetBytesRead = mEthernetBytesWritten = 0; + mCellBytesRead = mCellBytesWritten = 0; + return NS_OK; +} + +class DataUsageEvent : public nsRunnable +{ +public: + explicit DataUsageEvent(nsIInterfaceRequestor *cb, + uint64_t bytesRead, + uint64_t bytesWritten) + : mCB(cb), mRead(bytesRead), mWritten(bytesWritten) { } + + NS_IMETHOD Run() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + if (gHttpHandler) + gHttpHandler->UpdateDataUsage(mCB, mRead, mWritten); + return NS_OK; + } + +private: + ~DataUsageEvent() { } + nsCOMPtr mCB; + uint64_t mRead; + uint64_t mWritten; +}; + +void +nsHttpHandler::UpdateDataUsage(nsIInterfaceRequestor *cb, + uint64_t bytesRead, uint64_t bytesWritten) +{ + if (!IsTelemetryEnabled()) return; - // If B2G requires a similar mechanism nsINetworkManager, currently only avail - // on B2G, contains the necessary information on wifi and gateway + if (!NS_IsMainThread()) { + nsRefPtr event = new DataUsageEvent(cb, bytesRead, bytesWritten); + NS_DispatchToMainThread(event); + return; + } + + bool isEthernet = true; + + if (NS_FAILED(GetNetworkEthernetInfo(cb, &isEthernet))) { + // without a window it is hard for android to determine the network type + // so on failures we will just use the last value + if (!mNetworkTypeKnown) + return; + isEthernet = mNetworkTypeWasEthernet; + } + + if (isEthernet) { + mEthernetBytesRead += bytesRead; + mEthernetBytesWritten += bytesWritten; + } else { + mCellBytesRead += bytesRead; + mCellBytesWritten += bytesWritten; + } +} + +nsresult +nsHttpHandler::GetNetworkEthernetInfo(nsIInterfaceRequestor *cb, + bool *aEthernet) +{ + NS_ENSURE_ARG_POINTER(aEthernet); + + nsresult rv = GetNetworkEthernetInfoInner(cb, aEthernet); + if (NS_SUCCEEDED(rv)) { + mNetworkTypeKnown = true; + mNetworkTypeWasEthernet = *aEthernet; + } + return rv; +} + +// aEthernet and aGateway are required out parameters +// on b2g and desktop gateway cannot be determined yet and +// this function returns ERROR_NOT_IMPLEMENTED. +nsresult +nsHttpHandler::GetNetworkInfo(nsIInterfaceRequestor *cb, + bool *aEthernet, + uint32_t *aGateway) +{ + NS_ENSURE_ARG_POINTER(aEthernet); + NS_ENSURE_ARG_POINTER(aGateway); + + nsresult rv = GetNetworkInfoInner(cb, aEthernet, aGateway); + if (NS_SUCCEEDED(rv)) { + mNetworkTypeKnown = true; + mNetworkTypeWasEthernet = *aEthernet; + } + return rv; +} + +nsresult +nsHttpHandler::GetNetworkInfoInner(nsIInterfaceRequestor *cb, + bool *aEthernet, + uint32_t *aGateway) +{ + NS_ENSURE_ARG_POINTER(aEthernet); + NS_ENSURE_ARG_POINTER(aGateway); + + *aGateway = 0; + *aEthernet = true; + +#if defined(MOZ_WIDGET_GONK) + // b2g only allows you to ask for ethernet or not right now. + return NS_ERROR_NOT_IMPLEMENTED; +#endif + +#if defined(ANDROID) + if (!cb) + return NS_ERROR_FAILURE; nsCOMPtr domWindow; cb->GetInterface(NS_GET_IID(nsIDOMWindow), getter_AddRefs(domWindow)); if (!domWindow) - return; + return NS_ERROR_FAILURE; nsCOMPtr domNavigator; domWindow->GetNavigator(getter_AddRefs(domNavigator)); nsCOMPtr networkNavigator = do_QueryInterface(domNavigator); if (!networkNavigator) - return; + return NS_ERROR_FAILURE; nsCOMPtr mozConnection; networkNavigator->GetMozConnection(getter_AddRefs(mozConnection)); nsCOMPtr networkProperties = do_QueryInterface(mozConnection); if (!networkProperties) + return NS_ERROR_FAILURE; + + nsresult rv; + rv = networkProperties->GetDhcpGateway(aGateway); + if (NS_FAILED(rv)) + return rv; + + return networkProperties->GetIsWifi(aEthernet); +#endif + + // desktop does not currently know about the gateway + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +nsHttpHandler::GetNetworkEthernetInfoInner(nsIInterfaceRequestor *cb, + bool *aEthernet) +{ + *aEthernet = true; + +#if defined(MOZ_WIDGET_GONK) + int32_t networkType; + nsCOMPtr networkManager = + do_GetService("@mozilla.org/network/manager;1"); + if (!networkManager) + return NS_ERROR_FAILURE; + if (NS_FAILED(networkManager->GetPreferredNetworkType(&networkType))) + return NS_ERROR_FAILURE; + *aEthernet = networkType == nsINetworkInterface::NETWORK_TYPE_WIFI; + return NS_OK; +#endif + +#if defined(ANDROID) + if (!cb) + return NS_ERROR_FAILURE; + + nsCOMPtr domWindow; + cb->GetInterface(NS_GET_IID(nsIDOMWindow), getter_AddRefs(domWindow)); + if (!domWindow) + return NS_ERROR_FAILURE; + + nsCOMPtr domNavigator; + domWindow->GetNavigator(getter_AddRefs(domNavigator)); + nsCOMPtr networkNavigator = + do_QueryInterface(domNavigator); + if (!networkNavigator) + return NS_ERROR_FAILURE; + + nsCOMPtr mozConnection; + networkNavigator->GetMozConnection(getter_AddRefs(mozConnection)); + nsCOMPtr networkProperties = + do_QueryInterface(mozConnection); + if (!networkProperties) + return NS_ERROR_FAILURE; + + return networkProperties->GetIsWifi(aEthernet); +#endif + + // desktop assumes never on cell data + *aEthernet = true; + return NS_OK; +} + +void +nsHttpHandler::TickleWifi(nsIInterfaceRequestor *cb) +{ + if (!cb || !mWifiTickler) return; uint32_t gwAddress; bool isWifi; - nsresult rv; - rv = networkProperties->GetDhcpGateway(&gwAddress); - if (NS_SUCCEEDED(rv)) - rv = networkProperties->GetIsWifi(&isWifi); - if (NS_FAILED(rv)) - return; - - if (!gwAddress || !isWifi) + nsresult rv = GetNetworkInfo(cb, &isWifi, &gwAddress); + if (NS_FAILED(rv) || !gwAddress || !isWifi) return; mWifiTickler->SetIPV4Address(gwAddress); diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index ddf5601ab09..1daa12844d7 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -15,6 +15,7 @@ #include "nsCOMPtr.h" #include "nsWeakReference.h" +#include "nsIHttpDataUsage.h" #include "nsIHttpProtocolHandler.h" #include "nsIObserver.h" #include "nsISpeculativeConnect.h" @@ -50,6 +51,7 @@ class nsHttpHandler : public nsIHttpProtocolHandler , public nsIObserver , public nsSupportsWeakReference , public nsISpeculativeConnect + , public nsIHttpDataUsage { public: NS_DECL_THREADSAFE_ISUPPORTS @@ -58,6 +60,7 @@ public: NS_DECL_NSIHTTPPROTOCOLHANDLER NS_DECL_NSIOBSERVER NS_DECL_NSISPECULATIVECONNECT + NS_DECL_NSIHTTPDATAUSAGE nsHttpHandler(); virtual ~nsHttpHandler(); @@ -473,8 +476,30 @@ public: } private: + // for nsIHttpDataUsage + uint64_t mEthernetBytesRead; + uint64_t mEthernetBytesWritten; + uint64_t mCellBytesRead; + uint64_t mCellBytesWritten; + bool mNetworkTypeKnown; + bool mNetworkTypeWasEthernet; + nsRefPtr mWifiTickler; + nsresult GetNetworkEthernetInfo(nsIInterfaceRequestor *cb, + bool *ethernet); + nsresult GetNetworkEthernetInfoInner(nsIInterfaceRequestor *cb, + bool *ethernet); + nsresult GetNetworkInfo(nsIInterfaceRequestor *cb, + bool *ethernet, uint32_t *gw); + nsresult GetNetworkInfoInner(nsIInterfaceRequestor *cb, + bool *ethernet, uint32_t *gw); void TickleWifi(nsIInterfaceRequestor *cb); + +public: + // this is called to update the member variables used for nsIHttpDataUsage + // it can be called from any thread + void UpdateDataUsage(nsIInterfaceRequestor *cb, + uint64_t bytesRead, uint64_t bytesWritten); }; extern nsHttpHandler *gHttpHandler; diff --git a/netwerk/protocol/http/nsIHttpDataUsage.idl b/netwerk/protocol/http/nsIHttpDataUsage.idl new file mode 100644 index 00000000000..ecbb52976c9 --- /dev/null +++ b/netwerk/protocol/http/nsIHttpDataUsage.idl @@ -0,0 +1,27 @@ +/* -*- 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/. */ + +#include "nsISupports.idl" + +/** + * nsIHttpDataUsage contains counters for the amount of HTTP data transferred + * in and out of this session since the last time it was reset with the + * resetHttpDataUsage() method. These counters are normally reset on each + * telemetry ping. + * + * Data is split into ethernet and cell. ethernet includes wifi. + * + */ + +[scriptable, uuid(79dee3eb-9323-4d5c-b0a8-1baa18934d9e)] +interface nsIHttpDataUsage : nsISupports +{ + readonly attribute unsigned long long ethernetBytesRead; + readonly attribute unsigned long long ethernetBytesWritten; + readonly attribute unsigned long long cellBytesRead; + readonly attribute unsigned long long cellBytesWritten; + + void resetHttpDataUsage(); +}; diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 5013ef7e074..6a01634312b 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -869,6 +869,34 @@ "kind": "boolean", "description": "Whether a HTTP base page load was over SSL or not." }, + "HTTPDATA_DAILY_ETHERNET_IN": { + "kind": "exponential", + "high": "10000", + "n_buckets": 200, + "extended_statistics_ok": true, + "description": "MB of http ethernet data recvd in one day" + }, + "HTTPDATA_DAILY_ETHERNET_OUT": { + "kind": "exponential", + "high": "10000", + "n_buckets": 200, + "extended_statistics_ok": true, + "description": "MB of http ethernet data sent in one day" + }, + "HTTPDATA_DAILY_CELL_IN": { + "kind": "exponential", + "high": "10000", + "n_buckets": 200, + "extended_statistics_ok": true, + "description": "MB of http cell data recvd in one day" + }, + "HTTPDATA_DAILY_CELL_OUT": { + "kind": "exponential", + "high": "10000", + "n_buckets": 200, + "extended_statistics_ok": true, + "description": "MB of http cell data sent in one day" + }, "SSL_HANDSHAKE_VERSION": { "kind": "enumerated", "n_values": 16,