Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
338 lines
10 KiB
JavaScript
Executable file
338 lines
10 KiB
JavaScript
Executable file
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
|
'use strict';
|
|
|
|
/* global uuid, dump, SettingsCache */
|
|
|
|
/**
|
|
* A simple ping that is kicked off on first time use
|
|
*/
|
|
(function(exports) {
|
|
var DEBUG = false;
|
|
|
|
const FTU_PING_ACTIVATION = 'ftu.pingActivation';
|
|
const FTU_PING_ENABLED = 'ftu.pingEnabled';
|
|
const FTU_PING_ID = 'ftu.pingID';
|
|
// We use these two variables for ping retrying count. In phone version, we
|
|
// use it for waiting for mobile connection.
|
|
const FTU_PING_MAX_NETWORK_FAILS = 'ftu.pingMaxNetworkFails';
|
|
const FTU_PING_NETWORK_FAIL_COUNT = 'ftu.pingNetworkFailCount';
|
|
const FTU_PING_URL = 'ftu.pingURL';
|
|
const FTU_PING_TIMEOUT = 'ftu.pingTimeout';
|
|
const FTU_PING_TRY_INTERVAL = 'ftu.pingTryInterval';
|
|
|
|
const DEFAULT_TRY_INTERVAL = 60 * 60 * 1000;
|
|
const DEFAULT_MAX_NETWORK_FAILS = 24;
|
|
const DEFAULT_PING_TIMEOUT = 60 * 1000;
|
|
|
|
// Used by the telemetry server to help identify the payload format
|
|
const PING_PAYLOAD_VERSION = 3;
|
|
|
|
// Settings to observe value changes for while the ping has not been sent
|
|
const OBSERVE_SETTINGS = ['deviceinfo.os',
|
|
'deviceinfo.software',
|
|
'deviceinfo.platform_build_id',
|
|
'deviceinfo.platform_version',
|
|
'deviceinfo.product_model',
|
|
'deviceinfo.firmware_revision',
|
|
'deviceinfo.hardware',
|
|
'app.update.channel'];
|
|
|
|
function FtuPing() {
|
|
}
|
|
|
|
FtuPing.prototype = {
|
|
// interval timer for first time ping
|
|
_pingTimer: null,
|
|
|
|
// time between ping tries
|
|
_tryInterval: DEFAULT_TRY_INTERVAL,
|
|
|
|
// URL for first time ping
|
|
_pingURL: null,
|
|
|
|
// Whether or not FTU ping is enabled
|
|
_pingEnabled: true,
|
|
|
|
// The number of times to wait for SIM / voice network data before sending
|
|
// the ping anyway
|
|
_maxNetworkFails: DEFAULT_MAX_NETWORK_FAILS,
|
|
|
|
// Number of network failures accrued
|
|
_networkFailCount: 0,
|
|
|
|
// Data used in ping
|
|
_pingData: {},
|
|
|
|
// Timeout for ping requests
|
|
_pingTimeout: DEFAULT_PING_TIMEOUT,
|
|
_settingObserver: null,
|
|
|
|
debug: function fp_debug(msg) {
|
|
if (DEBUG) {
|
|
dump('[FtuPing] ' + msg + '\n');
|
|
}
|
|
},
|
|
|
|
reset: function fp_reset() {
|
|
this._pingTimer = null;
|
|
this._tryInterval = DEFAULT_TRY_INTERVAL;
|
|
this._pingTimeout = DEFAULT_PING_TIMEOUT;
|
|
this._settingObserver = null;
|
|
|
|
this._pingURL = null;
|
|
this._pingEnabled = true;
|
|
|
|
this._maxNetworkFails = DEFAULT_MAX_NETWORK_FAILS;
|
|
this._networkFailCount = 0;
|
|
|
|
this._pingData = {};
|
|
},
|
|
|
|
initSettings: function fp_initSettings(callback) {
|
|
this.reset();
|
|
|
|
this._pingData.ver = PING_PAYLOAD_VERSION;
|
|
this._pingData.screenHeight = window.screen.height;
|
|
this._pingData.screenWidth = window.screen.width;
|
|
this._pingData.devicePixelRatio = window.devicePixelRatio;
|
|
this._pingData.locale = window.navigator.language;
|
|
|
|
var self = this;
|
|
this.getAsyncStorageItems([FTU_PING_ID, FTU_PING_ACTIVATION,
|
|
FTU_PING_ENABLED,
|
|
FTU_PING_NETWORK_FAIL_COUNT], function(items) {
|
|
|
|
self._pingData.pingID = items[FTU_PING_ID];
|
|
self._pingData.activationTime = items[FTU_PING_ACTIVATION];
|
|
self._pingEnabled = items[FTU_PING_ENABLED];
|
|
|
|
if (!self._pingData.pingID) {
|
|
self._pingData.pingID = uuid();
|
|
window.asyncStorage.setItem(FTU_PING_ID, self._pingData.pingID);
|
|
}
|
|
|
|
if (!self._pingData.activationTime) {
|
|
self._pingData.activationTime = Date.now();
|
|
window.asyncStorage.setItem(FTU_PING_ACTIVATION,
|
|
self._pingData.activationTime);
|
|
}
|
|
|
|
if (self._pingEnabled === null) {
|
|
self._pingEnabled = true;
|
|
}
|
|
|
|
if (typeof(items[FTU_PING_NETWORK_FAIL_COUNT]) === 'number') {
|
|
self._networkFailCount = items[FTU_PING_NETWORK_FAIL_COUNT];
|
|
}
|
|
|
|
var allSettings = [FTU_PING_URL, FTU_PING_TRY_INTERVAL,
|
|
FTU_PING_TIMEOUT, FTU_PING_MAX_NETWORK_FAILS].
|
|
concat(OBSERVE_SETTINGS);
|
|
|
|
self.getSettings(allSettings, function(settings) {
|
|
self._pingURL = settings[FTU_PING_URL];
|
|
self._tryInterval = settings[FTU_PING_TRY_INTERVAL] ||
|
|
self._tryInterval;
|
|
self._pingTimeout = settings[FTU_PING_TIMEOUT] || self._pingTimeout;
|
|
self._maxNetworkFails = settings[FTU_PING_MAX_NETWORK_FAILS] ||
|
|
self._maxNetworkFails;
|
|
|
|
if (!self._settingObserver) {
|
|
self._settingObserver = self.onSettingChanged.bind(self);
|
|
}
|
|
|
|
OBSERVE_SETTINGS.forEach(function(observeSetting) {
|
|
self._pingData[observeSetting] = settings[observeSetting];
|
|
SettingsCache.observe(observeSetting, null, function(value) {
|
|
self._settingObserver(observeSetting, value);
|
|
});
|
|
});
|
|
|
|
if (callback) {
|
|
callback();
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
getAsyncStorageItems: function fp_getAsyncStorageItems(itemKeys, callback) {
|
|
var itemsLeft = itemKeys.length;
|
|
var items = {};
|
|
itemKeys.forEach(function(key) {
|
|
window.asyncStorage.getItem(key, function(value) {
|
|
itemsLeft--;
|
|
items[key] = value;
|
|
if (itemsLeft === 0 && callback) {
|
|
callback(items);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
getSettings: function fp_getSettings(settingKeys, callback) {
|
|
var settingsLeft = settingKeys.length;
|
|
var settings = {};
|
|
settingKeys.forEach(function(key) {
|
|
SettingsCache.get(key, function(value) {
|
|
settingsLeft--;
|
|
settings[key] = value;
|
|
if (settingsLeft === 0 && callback) {
|
|
callback(settings);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
ensurePing: function fp_ensurePing() {
|
|
this.initSettings(this.startPing.bind(this));
|
|
},
|
|
|
|
onSettingChanged: function fp_onSettingChanged(key, value) {
|
|
this._pingData[key] = value;
|
|
},
|
|
|
|
startPing: function fp_startPing() {
|
|
if (this._pingEnabled === false) {
|
|
this.debug('FTU ping disabled');
|
|
return;
|
|
}
|
|
|
|
if (!this._pingURL) {
|
|
this.debug('No FTU ping URL');
|
|
return;
|
|
}
|
|
if (this._pingTimer !== null) {
|
|
return;
|
|
}
|
|
|
|
this.debug('Starting FTU ping');
|
|
this._pingTimer = setInterval(this.tryPing.bind(this), this._tryInterval);
|
|
},
|
|
|
|
tryPing: function fp_tryPing() {
|
|
try {
|
|
if (this._networkFailCount >= this._maxNetworkFails) {
|
|
this.debug('Max voice network failures reached, pinging anyway!');
|
|
return false;
|
|
}
|
|
|
|
if (!this._pingData['deviceinfo.os']) {
|
|
this.debug('No OS information, holding off');
|
|
return false;
|
|
}
|
|
|
|
this.ping();
|
|
return true;
|
|
} catch (e) {
|
|
this.debug('Error while trying FTU ping: ' + e);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
generatePingURL: function fp_generatePingURL() {
|
|
var version = this._pingData['deviceinfo.platform_version'] || 'unknown';
|
|
var updateChannel = this._pingData['app.update.channel'] || 'unknown';
|
|
var buildId = this._pingData['deviceinfo.platform_build_id'] || 'unknown';
|
|
|
|
var uriParts = [
|
|
this._pingURL,
|
|
encodeURIComponent(this._pingData.pingID),
|
|
'ftu', // 'reason'
|
|
'FirefoxOS', // 'appName'
|
|
encodeURIComponent(version), // 'appVersion'
|
|
encodeURIComponent(updateChannel), // 'appUpdateChannel'
|
|
encodeURIComponent(buildId) // 'appBuildID'
|
|
];
|
|
|
|
return uriParts.join('/');
|
|
},
|
|
|
|
ping: function fp_ping() {
|
|
var url = this.generatePingURL();
|
|
this._pingData.pingTime = Date.now();
|
|
|
|
if (DEBUG) {
|
|
this.debug(url);
|
|
this.debug(JSON.stringify(this._pingData));
|
|
}
|
|
|
|
var xhr = new XMLHttpRequest({ mozSystem: true, mozAnon: true });
|
|
xhr.timeout = this._pingTimeout;
|
|
|
|
var self = this;
|
|
xhr.onload = function() {
|
|
self.pingSuccess(xhr.responseText);
|
|
};
|
|
|
|
xhr.ontimeout = function() {
|
|
self.pingError('Timed out');
|
|
};
|
|
|
|
xhr.onerror = function() {
|
|
self.pingError(xhr.statusText);
|
|
};
|
|
|
|
xhr.open('POST', url, true);
|
|
xhr.setRequestHeader('Content-type', 'application/json');
|
|
xhr.responseType = 'text';
|
|
xhr.send(JSON.stringify(this._pingData));
|
|
},
|
|
|
|
pingSuccess: function fp_pingSuccess(result) {
|
|
if (result !== 'OK') {
|
|
this.debug('Ping response unexpected: ' + result);
|
|
return;
|
|
}
|
|
|
|
this._pingEnabled = false;
|
|
window.asyncStorage.setItem(FTU_PING_ENABLED, false);
|
|
// XXX: we don't have unobserve
|
|
|
|
// var settings = window.navigator.mozSettings;
|
|
// OBSERVE_SETTINGS.forEach(function(setting) {
|
|
// settings.removeObserver(setting, this._settingObserver);
|
|
// }, this);
|
|
|
|
clearInterval(this._pingTimer);
|
|
},
|
|
|
|
pingError: function fp_pingError(message) {
|
|
this.debug('Ping error: ' + message);
|
|
this._networkFailCount++;
|
|
window.asyncStorage.setItem(FTU_PING_NETWORK_FAIL_COUNT,
|
|
this._networkFailCount);
|
|
},
|
|
|
|
isEnabled: function fp_isEnabled() {
|
|
return this._pingEnabled;
|
|
},
|
|
|
|
getPingURL: function fp_getPingURL() {
|
|
return this._pingURL;
|
|
},
|
|
|
|
getPingData: function fp_getPingData() {
|
|
return this._pingData;
|
|
},
|
|
|
|
getNetworkFailCount: function fp_getNetworkFailCount() {
|
|
return this._networkFailCount;
|
|
},
|
|
|
|
getTryInterval: function fp_getTryInterval() {
|
|
return this._tryInterval;
|
|
},
|
|
|
|
getPingTimeout: function fp_getPingTimeout() {
|
|
return this._pingTimeout;
|
|
},
|
|
|
|
getMaxNetworkFails: function fp_getMaxNetworkFails() {
|
|
return this._maxNetworkFails;
|
|
}
|
|
};
|
|
|
|
exports.FtuPing = FtuPing;
|
|
}(window));
|