Bug 855951 - Collect and save TX/RX traffic amounts of TCP connections per-App, r=gene, mcmanus

This commit is contained in:
Ethan Tseng 2013-08-29 14:15:08 +08:00
Родитель 0199f985ae
Коммит 9ea9589875
7 изменённых файлов: 115 добавлений и 11 удалений

Просмотреть файл

@ -27,7 +27,7 @@ interface nsISocketTransport;
// Once bug 723206 will be fixed, this method could be replaced by
// arguments when instantiating a TCPSocket object. For example it will
// be possible to do (similarly to the WebSocket API):
// var s = new MozTCPSocket(host, port);
// var s = new MozTCPSocket(host, port);
// Bug 797561 - Expose a server tcp socket API to web applications
@ -215,7 +215,7 @@ interface nsIDOMTCPSocket : nsISupports
* Needed to account for multiple possible types that can be provided to
* the socket callbacks as arguments.
*/
[scriptable, uuid(0baa1be1-6a88-4f85-a6c8-29e95f35c122)]
[scriptable, uuid(234c664c-3d6c-4859-b45c-4e9a98cb5bdc)]
interface nsITCPSocketInternal : nsISupports {
// Trigger the callback for |type| and provide a DOMError() object with the given data
void callListenerError(in DOMString type, in DOMString name);
@ -245,17 +245,20 @@ interface nsITCPSocketInternal : nsISupports {
// Create a DOM socket on the child side
// This is called when the socket is accepted on the parent side.
//
//
// @param socketChild
// The socket child object for the IPC implementation.
// @param binaryType
// "arraybuffer" to use ArrayBuffer instances
// "arraybuffer" to use ArrayBuffer instances
// in the ondata callback and as the argument to send.
// @param window
// An object to create ArrayBuffer for this window. See Bug 831107.
nsIDOMTCPSocket createAcceptedChild(in nsITCPSocketChild socketChild,
in DOMString binaryType,
in DOMString binaryType,
in nsIDOMWindow window);
// Set App ID.
void setAppId(in unsigned long appId);
};
/**

Просмотреть файл

@ -38,12 +38,13 @@ interface nsITCPSocketParent : nsISupports
// Intermediate class to handle sending multiple possible data types
// and kicking off the chrome process socket object's connection.
[scriptable, uuid(38bec1ed-b863-40dd-ba69-7bd92e568ee3)]
[scriptable, uuid(be67b1b8-03b0-4171-a791-d004458021b6)]
interface nsITCPSocketIntermediary : nsISupports {
// Open the connection to the server with the given parameters
nsIDOMTCPSocket open(in nsITCPSocketParent parent,
in DOMString host, in unsigned short port,
in boolean useSSL, in DOMString binaryType);
in boolean useSSL, in DOMString binaryType,
in unsigned long appId);
// Listen on a port
nsIDOMTCPServerSocket listen(in nsITCPServerSocketParent parent,

Просмотреть файл

@ -37,6 +37,7 @@ const kCLOSED = 'closed';
const kRESUME_ERROR = 'Calling resume() on a connection that was not suspended.';
const BUFFER_SIZE = 65536;
const NETWORK_STATS_THRESHOLD = 65536;
// XXX we have no TCPError implementation right now because it's really hard to
// do on b2g18. On mozilla-central we want a proper TCPError that ideally
@ -161,6 +162,14 @@ TCPSocket.prototype = {
_waitingForStartTLS: false,
_pendingDataAfterStartTLS: [],
#ifdef MOZ_WIDGET_GONK
// Network statistics (Gonk-specific feature)
_txBytes: 0,
_rxBytes: 0,
_appId: Ci.nsIScriptSecurityManager.NO_APP_ID,
_connectionType: Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN,
#endif
// Public accessors.
get readyState() {
return this._readyState;
@ -315,6 +324,38 @@ TCPSocket.prototype = {
BUFFER_SIZE, /* close source*/ false, /* close sink */ false);
},
#ifdef MOZ_WIDGET_GONK
// Helper method for collecting network statistics.
// Note this method is Gonk-specific.
_saveNetworkStats: function ts_saveNetworkStats(enforce) {
if (this._txBytes <= 0 && this._rxBytes <= 0) {
// There is no traffic at all. No need to save statistics.
return;
}
// If "enforce" is false, the traffic amount is saved to NetworkStatsServiceProxy
// only when the total amount exceeds the predefined threshold value.
// The purpose is to avoid too much overhead for collecting statistics.
let totalBytes = this._txBytes + this._rxBytes;
if (!enforce && totalBytes < NETWORK_STATS_THRESHOLD) {
return;
}
let nssProxy = Cc["@mozilla.org/networkstatsServiceProxy;1"]
.getService(Ci.nsINetworkStatsServiceProxy);
if (!nssProxy) {
LOG("Error: Ci.nsINetworkStatsServiceProxy service is not available.");
return;
}
nssProxy.saveAppStats(this._appId, this._connectionType, Date.now(),
this._rxBytes, this._txBytes);
// Reset the counters once the statistics is saved to NetworkStatsServiceProxy.
this._txBytes = this._rxBytes = 0;
},
// End of helper method for network statistics.
#endif
callListener: function ts_callListener(type, data) {
if (!this["on" + type])
return;
@ -371,6 +412,14 @@ TCPSocket.prototype = {
return that;
},
setAppId: function ts_setAppId(appId) {
#ifdef MOZ_WIDGET_GONK
this._appId = appId;
#else
// Do nothing because _appId only exists on Gonk-specific platform.
#endif
},
/* end nsITCPSocketInternal methods */
initWindowless: function ts_initWindowless() {
@ -479,6 +528,17 @@ TCPSocket.prototype = {
let transport = that._transport = this._createTransport(host, port, that._ssl);
transport.setEventSink(that, Services.tm.currentThread);
that._initStream(that._binaryType);
#ifdef MOZ_WIDGET_GONK
// Set _connectionType, which is only required for network statistics.
// Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is
// Gonk-specific.
let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
if (networkManager && networkManager.active) {
that._connectionType = networkManager.active.type;
}
#endif
return that;
},
@ -589,6 +649,13 @@ TCPSocket.prototype = {
}
this._ensureCopying();
#ifdef MOZ_WIDGET_GONK
// Collect transmitted amount for network statistics.
this._txBytes += length;
this._saveNetworkStats(false);
#endif
return bufferNotFull;
},
@ -621,6 +688,12 @@ TCPSocket.prototype = {
},
_maybeReportErrorAndCloseIfOpen: function(status) {
#ifdef MOZ_WIDGET_GONK
// Save network statistics once the connection is closed.
// For now this function is Gonk-specific.
this._saveNetworkStats(true);
#endif
// If we're closed, we've already reported the error or just don't need to
// report the error.
if (this._readyState === kCLOSED)
@ -813,6 +886,12 @@ TCPSocket.prototype = {
} else {
this.callListener("data", this._inputStreamScriptable.read(count));
}
#ifdef MOZ_WIDGET_GONK
// Collect received amount for network statistics.
this._rxBytes += count;
this._saveNetworkStats(false);
#endif
},
classID: Components.ID("{cda91b22-6472-11e1-aa11-834fec09cd0a}"),

Просмотреть файл

@ -12,6 +12,8 @@
#include "mozilla/AppProcessChecker.h"
#include "mozilla/net/NeckoCommon.h"
#include "mozilla/net/PNeckoParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/TabParent.h"
namespace IPC {
@ -91,6 +93,15 @@ TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bo
return true;
}
// Obtain App ID
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
const PContentParent *content = Manager()->Manager();
const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent();
if (browsers.Length() > 0) {
TabParent *tab = static_cast<TabParent*>(browsers[0]);
appId = tab->OwnAppId();
}
nsresult rv;
mIntermediary = do_CreateInstance("@mozilla.org/tcp-socket-intermediary;1", &rv);
if (NS_FAILED(rv)) {
@ -98,7 +109,8 @@ TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bo
return true;
}
rv = mIntermediary->Open(this, aHost, aPort, aUseSSL, aBinaryType, getter_AddRefs(mSocket));
rv = mIntermediary->Open(this, aHost, aPort, aUseSSL, aBinaryType, appId,
getter_AddRefs(mSocket));
if (NS_FAILED(rv) || !mSocket) {
FireInteralError(this, __LINE__);
return true;

Просмотреть файл

@ -30,12 +30,17 @@ TCPSocketParentIntermediary.prototype = {
);
},
open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType) {
open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType, aAppId) {
let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
let socket = baseSocket.open(aHost, aPort, {useSecureTransport: aUseSSL, binaryType: aBinaryType});
if (!socket)
return null;
let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal);
if (socketInternal) {
socketInternal.setAppId(aAppId);
}
// Handlers are set to the JS-implemented socket object on the parent side.
this._setCallbacks(aParentSide, socket);
return socket;

Просмотреть файл

@ -32,11 +32,14 @@ if CONFIG['MOZ_B2G_RIL']:
EXTRA_COMPONENTS += [
'TCPServerSocket.js',
'TCPSocket.js',
'TCPSocket.manifest',
'TCPSocketParentIntermediary.js',
]
EXTRA_PP_COMPONENTS += [
'TCPSocket.js',
]
if CONFIG['MOZ_B2G_RIL']:
EXTRA_COMPONENTS += [
'NetworkStatsManager.js',

Просмотреть файл

@ -7,7 +7,7 @@
/**
* Information about networks that is exposed to network manager API consumers.
*/
[scriptable, uuid(04fe5049-1ea8-4b4f-8c27-d23cd24611bb)]
[scriptable, uuid(f4cf9d88-f962-4d29-9baa-fb295dad387b)]
interface nsINetworkInterface : nsISupports
{
const long NETWORK_STATE_UNKNOWN = -1;
@ -24,6 +24,7 @@ interface nsINetworkInterface : nsISupports
*/
readonly attribute long state;
const long NETWORK_TYPE_UNKNOWN = -1;
const long NETWORK_TYPE_WIFI = 0;
const long NETWORK_TYPE_MOBILE = 1;
const long NETWORK_TYPE_MOBILE_MMS = 2;