Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/telemetry/geckoview/TelemetryGeckoViewPersistence.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "TelemetryGeckoViewPersistence.h"
8
9
#include "jsapi.h"
10
#include "js/JSON.h"
11
#include "mozilla/ErrorNames.h"
12
#include "mozilla/JSONWriter.h"
13
#include "mozilla/Path.h"
14
#include "mozilla/Preferences.h"
15
#include "mozilla/ScopeExit.h"
16
#include "mozilla/StaticPtr.h"
17
#include "mozilla/SystemGroup.h"
18
#include "mozilla/dom/ScriptSettings.h" // for AutoJSAPI
19
#include "mozilla/dom/SimpleGlobalObject.h"
20
#include "nsDirectoryServiceDefs.h"
21
#include "nsIFile.h"
22
#include "nsIInputStream.h"
23
#include "nsIObserverService.h"
24
#include "nsIOutputStream.h"
25
#include "nsISafeOutputStream.h"
26
#include "nsITimer.h"
27
#include "nsLocalFile.h"
28
#include "nsNetUtil.h"
29
#include "nsXULAppAPI.h"
30
#include "prenv.h"
31
#include "prio.h"
32
#include "core/TelemetryScalar.h"
33
#include "core/TelemetryHistogram.h"
34
#include "xpcpublic.h"
35
36
using mozilla::GetErrorName;
37
using mozilla::MakeScopeExit;
38
using mozilla::Preferences;
39
using mozilla::StaticRefPtr;
40
using mozilla::SystemGroup;
41
using mozilla::TaskCategory;
42
using mozilla::dom::AutoJSAPI;
43
using mozilla::dom::SimpleGlobalObject;
44
45
using PathChar = mozilla::filesystem::Path::value_type;
46
using PathCharPtr = const PathChar*;
47
48
// Enable logging by default on Debug builds.
49
#ifdef DEBUG
50
// If we're building for Android, use the provided logging facility.
51
#ifdef MOZ_WIDGET_ANDROID
52
#include <android/log.h>
53
#define ANDROID_LOG(fmt, ...) \
54
  __android_log_print(ANDROID_LOG_DEBUG, "Telemetry", fmt, ##__VA_ARGS__)
55
#else
56
// If we're building for other platforms (e.g. for running test coverage), try
57
// to print something anyway.
58
#define ANDROID_LOG(...) printf_stderr("\n**** TELEMETRY: " __VA_ARGS__)
59
#endif // MOZ_WIDGET_ANDROID
60
#else
61
// No-op on Release builds.
62
#define ANDROID_LOG(...)
63
#endif // DEBUG
64
65
// The Gecko runtime can be killed at anytime. Moreover, we can
66
// have very short lived sessions. The persistence timeout governs
67
// how frequently measurements are saved to disk.
68
const uint32_t kDefaultPersistenceTimeoutMs = 60 * 1000; // 60s
69
70
// The name of the persistence file used for saving the
71
// measurements.
72
const char16_t kPersistenceFileName[] = u"gv_measurements.json";
73
74
// This topic is notified and propagated up to the application to
75
// make sure it knows that data loading has complete and that snapshotting
76
// can now be performed.
77
const char kLoadCompleteTopic[] = "internal-telemetry-geckoview-load-complete";
78
79
// The timer used for persisting measurements data.
80
nsITimer* gPersistenceTimer;
81
// The worker thread to perform persistence.
82
StaticRefPtr<nsIThread> gPersistenceThread;
83
84
namespace {
85
86
void PersistenceThreadPersist();
87
88
/**
89
+ * The helper class used by mozilla::JSONWriter to
90
+ * serialize the JSON structure to a file.
91
+ */
92
class StreamingJSONWriter : public mozilla::JSONWriteFunc
93
{
94
public:
95
  nsresult Open(nsCOMPtr<nsIFile> aOutFile)
96
0
  {
97
0
    MOZ_ASSERT(!mStream, "Open must not be called twice");
98
0
    nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(mStream), aOutFile);
99
0
    NS_ENSURE_SUCCESS(rv, rv);
100
0
    return NS_OK;
101
0
  }
102
103
  nsresult Close()
104
0
  {
105
0
    MOZ_ASSERT(mStream, "Close must be called on an already opened stream");
106
0
    // We don't need to care too much about checking if count matches
107
0
    // the length of aData: Finish() will do that for us and fail if
108
0
    // Write did not persist all the data or mStream->Close() failed.
109
0
    // Note that |nsISafeOutputStream| will write to a temp file and only
110
0
    // overwrite the destination if no error was reported.
111
0
    nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(mStream);
112
0
    MOZ_ASSERT(safeStream);
113
0
    return safeStream->Finish();
114
0
  }
115
116
  void Write(const char* aStr) override
117
0
  {
118
0
    uint32_t count;
119
0
    mozilla::Unused << mStream->Write(aStr, strlen(aStr), &count);
120
0
  }
121
122
private:
123
  nsCOMPtr<nsIOutputStream> mStream;
124
};
125
126
/**
127
 * Get the path to the Android Data dir.
128
 *
129
 * @param {nsTString<PathChar>} aOutDir - the variable holding the path.
130
 * @return {nsresult} NS_OK if the data dir path was found, a failure value otherwise.
131
 */
132
nsresult
133
GetAndroidDataDir(nsTString<PathChar>& aOutDir)
134
0
{
135
0
  // This relies on the Java environment to set the location of the
136
0
  // cache directory. If that happens, the following variable is set.
137
0
  // This should always be the case.
138
0
  const char *dataDir = PR_GetEnv("MOZ_ANDROID_DATA_DIR");
139
0
  if (!dataDir || !*dataDir) {
140
0
    ANDROID_LOG("GetAndroidDataDir - Cannot find the data directory in the environment.");
141
0
    return NS_ERROR_FAILURE;
142
0
  }
143
0
144
0
  aOutDir.AssignASCII(dataDir);
145
0
  return NS_OK;
146
0
}
147
148
/**
149
 * Get the path to the persistence file in the Android Data dir.
150
 *
151
 * @param {nsCOMPtr<nsIFile>} aOutFile - the nsIFile pointer holding the file info.
152
 * @return {nsresult} NS_OK if the persistence file was found, a failure value otherwise.
153
 */
154
nsresult
155
GetPersistenceFile(nsCOMPtr<nsIFile>& aOutFile)
156
0
{
157
0
  nsTString<PathChar> dataDir;
158
0
  nsresult rv = GetAndroidDataDir(dataDir);
159
0
  NS_ENSURE_SUCCESS(rv, rv);
160
0
161
0
  // Append the extension to the filename.
162
0
  nsAutoString fileName;
163
0
  fileName.Assign(kPersistenceFileName);
164
0
165
0
  aOutFile = new nsLocalFile(dataDir);
166
0
  aOutFile->Append(fileName);
167
0
  ANDROID_LOG("GetPersistenceFile -  %s", aOutFile->HumanReadablePath().get());
168
0
  return NS_OK;
169
0
}
170
171
/**
172
 * Read and parses JSON content from a file.
173
 *
174
 * @param {nsCOMPtr<nsIFile>} aFile - the nsIFile handle to the file.
175
 * @param {nsACString} fileContent - the content of the file.
176
 * @return {nsresult} NS_OK if the file was correctly read, an error code otherwise.
177
 */
178
nsresult
179
ReadFromFile(const nsCOMPtr<nsIFile>& aFile, nsACString& fileContent)
180
0
{
181
0
  int64_t fileSize = 0;
182
0
  nsresult rv = aFile->GetFileSize(&fileSize);
183
0
  if (NS_FAILED(rv)) {
184
0
    return rv;
185
0
  }
186
0
187
0
  nsCOMPtr<nsIInputStream> inStream;
188
0
  rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream),
189
0
                                  aFile,
190
0
                                  PR_RDONLY);
191
0
  NS_ENSURE_SUCCESS(rv, rv);
192
0
193
0
  // Make sure to close the stream.
194
0
  auto scopedStreamClose = MakeScopeExit([inStream] { inStream->Close(); });
195
0
196
0
  rv = NS_ReadInputStreamToString(inStream, fileContent, fileSize);
197
0
  NS_ENSURE_SUCCESS(rv, rv);
198
0
199
0
  return NS_OK;
200
0
}
201
202
/**
203
 * Arms the persistence timer and instructs to run the persistence
204
 * task off the main thread.
205
 */
206
void
207
MainThreadArmPersistenceTimer()
208
0
{
209
0
  MOZ_ASSERT(NS_IsMainThread());
210
0
  ANDROID_LOG("MainThreadArmPersistenceTimer");
211
0
212
0
  // We won't have a persistence timer the first time this runs, so take
213
0
  // care of that.
214
0
  if (!gPersistenceTimer) {
215
0
    gPersistenceTimer =
216
0
      NS_NewTimer(SystemGroup::EventTargetFor(TaskCategory::Other)).take();
217
0
    if (!gPersistenceTimer) {
218
0
      ANDROID_LOG("MainThreadArmPersistenceTimer - Timer creation failed.");
219
0
      return;
220
0
    }
221
0
  }
222
0
223
0
  // Define the callback for the persistence timer: it will dispatch the persistence
224
0
  // task off the main thread. Once finished, it will trigger the timer again.
225
0
  nsTimerCallbackFunc timerCallback = [](nsITimer* aTimer, void* aClosure) {
226
0
    gPersistenceThread->Dispatch(NS_NewRunnableFunction("PersistenceThreadPersist",
227
0
      []() -> void { ::PersistenceThreadPersist(); }));
228
0
  };
229
0
230
0
  uint32_t timeout = Preferences::GetUint("toolkit.telemetry.geckoPersistenceTimeout",
231
0
                                          kDefaultPersistenceTimeoutMs);
232
0
233
0
  // Schedule the timer to automatically run and reschedule
234
0
  // every |kPersistenceTimeoutMs|.
235
0
  gPersistenceTimer->InitWithNamedFuncCallback(timerCallback,
236
0
                                               nullptr,
237
0
                                               timeout,
238
0
                                               nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
239
0
                                               "TelemetryGeckoViewPersistence::Persist");
240
0
}
241
242
/**
243
 * Parse the string data into a JSON structure, using
244
 * the native JS JSON parser.
245
 */
246
void
247
MainThreadParsePersistedProbes(const nsACString& aProbeData)
248
0
{
249
0
  // We're required to run on the main thread since we're using JS.
250
0
  MOZ_ASSERT(NS_IsMainThread());
251
0
  ANDROID_LOG("MainThreadParsePersistedProbes");
252
0
253
0
  // We need a JS context to run the parsing stuff in.
254
0
  JSObject* cleanGlobal =
255
0
    SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
256
0
  if (!cleanGlobal) {
257
0
    ANDROID_LOG("MainThreadParsePersistedProbes - Failed to create a JS global object");
258
0
    return;
259
0
  }
260
0
261
0
  AutoJSAPI jsapi;
262
0
  if (NS_WARN_IF(!jsapi.Init(cleanGlobal))) {
263
0
    ANDROID_LOG("MainThreadParsePersistedProbes - Failed to get JS API");
264
0
    return;
265
0
  }
266
0
267
0
  // Parse the JSON using the JS API.
268
0
  JS::RootedValue data(jsapi.cx());
269
0
  NS_ConvertUTF8toUTF16 utf16Content(aProbeData);
270
0
  if (!JS_ParseJSON(jsapi.cx(), utf16Content.BeginReading(), utf16Content.Length(), &data)) {
271
0
    ANDROID_LOG("MainThreadParsePersistedProbes - Failed to parse the persisted JSON");
272
0
    return;
273
0
  }
274
0
275
0
  // Get the data for the scalars.
276
0
  JS::RootedObject dataObj(jsapi.cx(), &data.toObject());
277
0
  JS::RootedValue scalarData(jsapi.cx());
278
0
  if (JS_GetProperty(jsapi.cx(), dataObj, "scalars", &scalarData)) {
279
0
    // If the data is an object, try to parse its properties. If not,
280
0
    // silently skip and try to load the other sections.
281
0
    if (!scalarData.isObject()
282
0
        || NS_FAILED(TelemetryScalar::DeserializePersistedScalars(jsapi.cx(), scalarData))) {
283
0
      ANDROID_LOG("MainThreadParsePersistedProbes - Failed to parse 'scalars'.");
284
0
      MOZ_ASSERT(!JS_IsExceptionPending(jsapi.cx()), "Parsers must suppress exceptions themselves");
285
0
    }
286
0
  } else {
287
0
    // Getting the "scalars" property failed, suppress the exception
288
0
    // and continue.
289
0
    JS_ClearPendingException(jsapi.cx());
290
0
  }
291
0
292
0
  JS::RootedValue keyedScalarData(jsapi.cx());
293
0
  if (JS_GetProperty(jsapi.cx(), dataObj, "keyedScalars", &keyedScalarData)) {
294
0
    // If the data is an object, try to parse its properties. If not,
295
0
    // silently skip and try to load the other sections.
296
0
    if (!keyedScalarData.isObject()
297
0
        || NS_FAILED(TelemetryScalar::DeserializePersistedKeyedScalars(jsapi.cx(), keyedScalarData))) {
298
0
      ANDROID_LOG("MainThreadParsePersistedProbes - Failed to parse 'keyedScalars'.");
299
0
      MOZ_ASSERT(!JS_IsExceptionPending(jsapi.cx()), "Parsers must suppress exceptions themselves");
300
0
    }
301
0
  } else {
302
0
    // Getting the "keyedScalars" property failed, suppress the exception
303
0
    // and continue.
304
0
    JS_ClearPendingException(jsapi.cx());
305
0
  }
306
0
307
0
  // Get the data for the histograms.
308
0
  JS::RootedValue histogramData(jsapi.cx());
309
0
  if (JS_GetProperty(jsapi.cx(), dataObj, "histograms", &histogramData)) {
310
0
    // If the data is an object, try to parse its properties. If not,
311
0
    // silently skip and try to load the other sections.
312
0
    nsresult rv = NS_OK;
313
0
    if (!histogramData.isObject()
314
0
        || NS_FAILED(rv = TelemetryHistogram::DeserializeHistograms(jsapi.cx(), histogramData))) {
315
0
      nsAutoCString errorName;
316
0
      GetErrorName(rv, errorName);
317
0
      ANDROID_LOG("MainThreadParsePersistedProbes - Failed to parse 'histograms', %s.",
318
0
                  errorName.get());
319
0
      MOZ_ASSERT(!JS_IsExceptionPending(jsapi.cx()), "Parsers must suppress exceptions themselves");
320
0
    }
321
0
  } else {
322
0
    // Getting the "histogramData" property failed, suppress the exception
323
0
    // and continue.
324
0
    JS_ClearPendingException(jsapi.cx());
325
0
  }
326
0
327
0
  // Get the data for the keyed histograms.
328
0
  JS::RootedValue keyedHistogramData(jsapi.cx());
329
0
  if (JS_GetProperty(jsapi.cx(), dataObj, "keyedHistograms", &keyedHistogramData)) {
330
0
    // If the data is an object, try to parse its properties. If not,
331
0
    // silently skip and try to load the other sections.
332
0
    nsresult rv = NS_OK;
333
0
    if (!keyedHistogramData.isObject()
334
0
        || NS_FAILED(rv = TelemetryHistogram::DeserializeKeyedHistograms(jsapi.cx(),
335
0
                                                                         keyedHistogramData))) {
336
0
      nsAutoCString errorName;
337
0
      GetErrorName(rv, errorName);
338
0
      ANDROID_LOG("MainThreadParsePersistedProbes - Failed to parse 'keyedHistograms', %s.",
339
0
                  errorName.get());
340
0
      MOZ_ASSERT(!JS_IsExceptionPending(jsapi.cx()), "Parsers must suppress exceptions themselves");
341
0
    }
342
0
  } else {
343
0
    // Getting the "keyedHistogramData" property failed, suppress the exception
344
0
    // and continue.
345
0
    JS_ClearPendingException(jsapi.cx());
346
0
  }
347
0
}
348
349
/**
350
 * The persistence worker function, meant to be run off the main thread.
351
 */
352
void
353
PersistenceThreadPersist()
354
0
{
355
0
  MOZ_ASSERT(XRE_IsParentProcess(), "We must only persist from the parent process.");
356
0
  MOZ_ASSERT(!NS_IsMainThread(), "This function must be called off the main thread.");
357
0
  ANDROID_LOG("PersistenceThreadPersist");
358
0
359
0
  // If the function completes or fails, make sure to spin up the persistence timer again.
360
0
  auto scopedArmTimer = MakeScopeExit([&] {
361
0
    NS_DispatchToMainThread(
362
0
      NS_NewRunnableFunction("MainThreadArmPersistenceTimer", []() -> void {
363
0
        MainThreadArmPersistenceTimer();
364
0
      }));
365
0
  });
366
0
367
0
  TelemetryScalar::Add(mozilla::Telemetry::ScalarID::TELEMETRY_PERSISTENCE_TIMER_HIT_COUNT, 1);
368
0
369
0
  nsCOMPtr<nsIFile> persistenceFile;
370
0
  if (NS_FAILED(GetPersistenceFile(persistenceFile))) {
371
0
    ANDROID_LOG("PersistenceThreadPersist - Failed to get the persistence file.");
372
0
    return;
373
0
  }
374
0
375
0
  // Open the persistence file.
376
0
  mozilla::UniquePtr<StreamingJSONWriter> jsonWriter =
377
0
    mozilla::MakeUnique<StreamingJSONWriter>();
378
0
379
0
  if (!jsonWriter || NS_FAILED(jsonWriter->Open(persistenceFile))) {
380
0
    ANDROID_LOG("PersistenceThreadPersist - There was an error opening the persistence file.");
381
0
    return;
382
0
  }
383
0
384
0
  // Build the JSON structure: give up the ownership of jsonWriter.
385
0
  mozilla::JSONWriter w(std::move(jsonWriter));
386
0
  w.Start();
387
0
388
0
  w.StartObjectProperty("scalars");
389
0
  if (NS_FAILED(TelemetryScalar::SerializeScalars(w))) {
390
0
    ANDROID_LOG("Persist - Failed to persist scalars.");
391
0
  }
392
0
  w.EndObject();
393
0
394
0
  w.StartObjectProperty("keyedScalars");
395
0
  if (NS_FAILED(TelemetryScalar::SerializeKeyedScalars(w))) {
396
0
    ANDROID_LOG("Persist - Failed to persist keyed scalars.");
397
0
  }
398
0
  w.EndObject();
399
0
400
0
  w.StartObjectProperty("histograms");
401
0
  if (NS_FAILED(TelemetryHistogram::SerializeHistograms(w))) {
402
0
    ANDROID_LOG("Persist - Failed to persist histograms.");
403
0
  }
404
0
  w.EndObject();
405
0
406
0
  w.StartObjectProperty("keyedHistograms");
407
0
  if (NS_FAILED(TelemetryHistogram::SerializeKeyedHistograms(w))) {
408
0
    ANDROID_LOG("Persist - Failed to persist keyed histograms.");
409
0
  }
410
0
  w.EndObject();
411
0
412
0
  // End the building process.
413
0
  w.End();
414
0
415
0
  // Android can kill us while we are writing to disk and, if that happens,
416
0
  // we end up with a corrupted json overwriting the old session data.
417
0
  // Luckily, |StreamingJSONWriter::Close| is smart enough to write to a
418
0
  // temporary file and only overwrite the original file if nothing bad happened.
419
0
  nsresult rv = static_cast<StreamingJSONWriter*>(w.WriteFunc())->Close();
420
0
  if (NS_FAILED(rv)) {
421
0
    ANDROID_LOG("PersistenceThreadPersist - There was an error writing to the persistence file.");
422
0
    return;
423
0
  }
424
0
}
425
426
/**
427
 * This function loads the persisted metrics from a JSON file
428
 * and adds them to the related storage. After it completes,
429
 * it spins up the persistence timer.
430
 *
431
 * Please note that this function is meant to be run off the
432
 * main-thread.
433
 */
434
void
435
PersistenceThreadLoadData()
436
0
{
437
0
  MOZ_ASSERT(XRE_IsParentProcess(), "We must only persist from the parent process.");
438
0
  MOZ_ASSERT(!NS_IsMainThread(), "We must perform I/O off the main thread.");
439
0
  ANDROID_LOG("PersistenceThreadLoadData");
440
0
441
0
  // If the function completes or fails, make sure to spin up the persistence timer.
442
0
  nsAutoCString fileContent;
443
0
  auto scopedArmTimer = MakeScopeExit([&] {
444
0
    NS_DispatchToMainThread(
445
0
      NS_NewRunnableFunction("MainThreadArmPersistenceTimer", [fileContent]() -> void {
446
0
        // Try to parse the probes if the file was not empty.
447
0
        if (!fileContent.IsEmpty()) {
448
0
          MainThreadParsePersistedProbes(fileContent);
449
0
        }
450
0
451
0
        TelemetryScalar::ApplyPendingOperations();
452
0
453
0
        // Arm the timer.
454
0
        MainThreadArmPersistenceTimer();
455
0
        // Notify that we're good to take snapshots!
456
0
        nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
457
0
        if (os) {
458
0
          os->NotifyObservers(nullptr, kLoadCompleteTopic, nullptr);
459
0
        }
460
0
      }));
461
0
  });
462
0
463
0
  // Attempt to load the persistence file. This could fail if we're not able
464
0
  // to allocate enough memory for the content. See bug 1460911.
465
0
  nsCOMPtr<nsIFile> persistenceFile;
466
0
  if (NS_FAILED(GetPersistenceFile(persistenceFile))
467
0
      || NS_FAILED(ReadFromFile(persistenceFile, fileContent))) {
468
0
    ANDROID_LOG("PersistenceThreadLoadData - Failed to load cache file at %s",
469
0
                persistenceFile->HumanReadablePath().get());
470
0
    return;
471
0
  }
472
0
}
473
474
} // anonymous namespace
475
476
// This namespace exposes testing only helpers to simplify writing
477
// gtest cases.
478
namespace TelemetryGeckoViewTesting {
479
480
void
481
TestDispatchPersist()
482
0
{
483
0
  gPersistenceThread->Dispatch(NS_NewRunnableFunction("Persist",
484
0
    []() -> void { ::PersistenceThreadPersist(); }));
485
0
}
486
487
} // GeckoViewTesting
488
489
void
490
TelemetryGeckoViewPersistence::InitPersistence()
491
0
{
492
0
  MOZ_ASSERT(NS_IsMainThread());
493
0
494
0
  if (gPersistenceThread) {
495
0
    ANDROID_LOG("Init must only be called once.");
496
0
    return;
497
0
  }
498
0
499
0
  // Only register the persistence timer in the parent process in
500
0
  // order to persist data for all the processes.
501
0
  if (!XRE_IsParentProcess()) {
502
0
    ANDROID_LOG("InitPersistence - Bailing out on child process.");
503
0
    return;
504
0
  }
505
0
506
0
  ANDROID_LOG("InitPersistence");
507
0
508
0
  // Spawn a new thread for handling GeckoView Telemetry persistence I/O.
509
0
  // We just spawn it once and re-use it later.
510
0
  nsCOMPtr<nsIThread> thread;
511
0
  nsresult rv =
512
0
    NS_NewNamedThread("TelemetryGVIO", getter_AddRefs(thread));
513
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
514
0
    ANDROID_LOG("InitPersistence -  Failed to instantiate the worker thread.");
515
0
    return;
516
0
  }
517
0
518
0
  gPersistenceThread = thread.forget();
519
0
520
0
  // From now on all scalar operations should be recorded.
521
0
  TelemetryScalar::DeserializationStarted();
522
0
523
0
  // Trigger the loading of the persistence data. After the function
524
0
  // completes it will automatically arm the persistence timer.
525
0
  gPersistenceThread->Dispatch(
526
0
    NS_NewRunnableFunction("PersistenceThreadLoadData", &PersistenceThreadLoadData));
527
0
}
528
529
void
530
TelemetryGeckoViewPersistence::DeInitPersistence()
531
0
{
532
0
  MOZ_ASSERT(NS_IsMainThread());
533
0
534
0
  // Bail out if this is not the parent process.
535
0
  if (!XRE_IsParentProcess()) {
536
0
    ANDROID_LOG("DeInitPersistence - Bailing out.");
537
0
    return;
538
0
  }
539
0
540
0
  // Even though we need to implement this function, it might end up
541
0
  // not being called: Android might kill us without notice to reclaim
542
0
  // our memory in case some other foreground application needs it.
543
0
  ANDROID_LOG("DeInitPersistence");
544
0
545
0
  if (gPersistenceThread) {
546
0
    gPersistenceThread->Shutdown();
547
0
    gPersistenceThread = nullptr;
548
0
  }
549
0
550
0
  if (gPersistenceTimer) {
551
0
    // Always make sure the timer is canceled.
552
0
    MOZ_ALWAYS_SUCCEEDS(gPersistenceTimer->Cancel());
553
0
    NS_RELEASE(gPersistenceTimer);
554
0
  }
555
0
}
556
557
void
558
TelemetryGeckoViewPersistence::ClearPersistenceData()
559
0
{
560
0
  // This can be run on any thread, as we just dispatch the persistence
561
0
  // task to the persistence thread.
562
0
  MOZ_ASSERT(gPersistenceThread);
563
0
564
0
  ANDROID_LOG("ClearPersistenceData");
565
0
566
0
  // Trigger clearing the persisted measurements off the main thread.
567
0
  gPersistenceThread->Dispatch(NS_NewRunnableFunction("ClearPersistedData",
568
0
    []() -> void {
569
0
      nsCOMPtr<nsIFile> persistenceFile;
570
0
      if (NS_FAILED(GetPersistenceFile(persistenceFile)) ||
571
0
          NS_FAILED(persistenceFile->Remove(false))) {
572
0
        ANDROID_LOG("ClearPersistenceData - Failed to remove the persistence file.");
573
0
        return;
574
0
      }
575
0
    }));
576
0
}