1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
45use super::{commands, FirefoxAccount};
6use crate::Result;
7use serde_derive::*;
8use sync_guid::Guid;
910impl FirefoxAccount {
11/// Gathers and resets telemetry for this account instance.
12 /// This should be considered a short-term solution to telemetry gathering
13 /// and should called whenever consumers expect there might be telemetry,
14 /// and it should submit the telemetry to whatever telemetry system is in
15 /// use (probably glean).
16 ///
17 /// The data is returned as a JSON string, which consumers should parse
18 /// forgivingly (eg, be tolerant of things not existing) to try and avoid
19 /// too many changes as telemetry comes and goes.
20pub fn gather_telemetry(&mut self) -> Result<String> {
21let telem = std::mem::replace(&mut self.telemetry, FxaTelemetry::new());
22Ok(serde_json::to_string(&telem)?)
23 }
24}
2526// A somewhat mixed-bag of all telemetry we want to collect. The idea is that
27// the app will "pull" telemetry via a new API whenever it thinks there might
28// be something to record.
29// It's considered a temporary solution until either we can record it directly
30// (eg, via glean) or we come up with something better.
31// Note that this means we'll lose telemetry if we crash between gathering it
32// here and the app submitting it, but that should be rare (in practice,
33// apps will submit it directly after an operation that generated telememtry)
3435/// The reason a tab/command was received.
36#[derive(Copy, Clone, Debug, Serialize)]
37#[serde(rename_all = "kebab-case")]
38pub enum ReceivedReason {
39/// A push notification for the command was received.
40Push,
41/// Discovered while handling a push notification for a later message.
42PushMissed,
43/// Explicit polling for missed commands.
44Poll,
45}
4647#[derive(Copy, Clone, Debug, Serialize)]
48pub enum Command {
49#[serde(rename = "send_tab")]
50SendTab,
51#[serde(rename = "close_tabs")]
52CloseTabs,
53}
5455#[derive(Debug, Serialize)]
56pub struct SentCommand {
57pub command: Command,
58pub flow_id: String,
59pub stream_id: String,
60}
6162impl SentCommand {
63pub fn for_send_tab() -> Self {
64Self::new(Command::SendTab)
65 }
6667pub fn for_close_tabs() -> Self {
68Self::new(Command::CloseTabs)
69 }
7071pub fn clone_with_new_stream_id(&self) -> Self {
72Self {
73 command: self.command,
74 flow_id: self.flow_id.clone(),
75 stream_id: Guid::random().into_string(),
76 }
77 }
7879fn new(command: Command) -> Self {
80Self {
81 command,
82 flow_id: Guid::random().into_string(),
83 stream_id: Guid::random().into_string(),
84 }
85 }
86}
8788#[derive(Debug, Serialize)]
89pub struct ReceivedCommand {
90pub command: Command,
91pub flow_id: String,
92pub stream_id: String,
93pub reason: ReceivedReason,
94}
9596impl ReceivedCommand {
97pub fn for_send_tab(payload: &commands::SendTabPayload, reason: ReceivedReason) -> Self {
98Self {
99 command: Command::SendTab,
100 flow_id: payload.flow_id.clone(),
101 stream_id: payload.stream_id.clone(),
102 reason,
103 }
104 }
105106pub fn for_close_tabs(payload: &commands::CloseTabsPayload, reason: ReceivedReason) -> Self {
107Self {
108 command: Command::SendTab,
109 flow_id: payload.flow_id.clone(),
110 stream_id: payload.stream_id.clone(),
111 reason,
112 }
113 }
114}
115116// We have a naive strategy to avoid unbounded memory growth - the intention
117// is that if any platform lets things grow to hit these limits, it's probably
118// never going to consume anything - so it doesn't matter what we discard (ie,
119// there's no good reason to have a smarter circular buffer etc)
120const MAX_TAB_EVENTS: usize = 200;
121122#[derive(Debug, Default, Serialize)]
123pub struct FxaTelemetry {
124 commands_sent: Vec<SentCommand>,
125 commands_received: Vec<ReceivedCommand>,
126}
127128impl FxaTelemetry {
129pub fn new() -> Self {
130 FxaTelemetry {
131 ..Default::default()
132 }
133 }
134135pub fn record_command_sent(&mut self, sent: SentCommand) {
136if self.commands_sent.len() < MAX_TAB_EVENTS {
137self.commands_sent.push(sent);
138 }
139 }
140141pub fn record_command_received(&mut self, recd: ReceivedCommand) {
142if self.commands_received.len() < MAX_TAB_EVENTS {
143self.commands_received.push(recd);
144 }
145 }
146}