24
24
#include " app/src/include/firebase/version.h"
25
25
#include " app/src/assert.h"
26
26
#include " app/src/log.h"
27
+ #include " app/src/mutex.h"
28
+ #include " app/src/time.h"
29
+ #include " app/src/thread.h"
30
+ #include " app/src/util.h"
27
31
#include " app/src/util_ios.h"
28
32
29
33
namespace firebase {
30
34
namespace analytics {
31
35
36
+ // Used to workaround b/143656277 and b/110166640
37
+ class AnalyticsDataResetter {
38
+ private:
39
+ enum ResetState {
40
+ kResetStateNone = 0 ,
41
+ kResetStateRequested ,
42
+ kResetStateRetry ,
43
+ };
44
+ public:
45
+ // Initialize the class.
46
+ AnalyticsDataResetter () : reset_state_(kResetStateNone ), reset_timestamp_(0 ) {}
47
+
48
+ // Reset analytics data.
49
+ void Reset () {
50
+ MutexLock lock (mutex_);
51
+ reset_timestamp_ = firebase::internal::GetTimestampEpoch ();
52
+ reset_state_ = kResetStateRequested ;
53
+ instance_id_ = util::StringFromNSString ([FIRAnalytics appInstanceID ]);
54
+ [FIRAnalytics resetAnalyticsData ];
55
+ }
56
+
57
+ // Get the instance ID, returning a non-empty string if it's valid or an empty string if it's
58
+ // still being reset.
59
+ std::string GetInstanceId () {
60
+ MutexLock lock (mutex_);
61
+ std::string current_instance_id = util::StringFromNSString ([FIRAnalytics appInstanceID ]);
62
+ uint64_t reset_time_elapsed_milliseconds = GetResetTimeElapsedMilliseconds ();
63
+ switch (reset_state_) {
64
+ case kResetStateNone :
65
+ break ;
66
+ case kResetStateRequested :
67
+ if (reset_time_elapsed_milliseconds >= kResetRetryIntervalMilliseconds ) {
68
+ // Firebase Analytics on iOS can take a while to initialize, in this case we try to reset
69
+ // again if the instance ID hasn't changed for a while.
70
+ reset_state_ = kResetStateRetry ;
71
+ reset_timestamp_ = firebase::internal::GetTimestampEpoch ();
72
+ [FIRAnalytics resetAnalyticsData ];
73
+ return std::string ();
74
+ }
75
+ FIREBASE_CASE_FALLTHROUGH;
76
+
77
+ case kResetStateRetry :
78
+ if ((current_instance_id.empty () || current_instance_id == instance_id_) &&
79
+ reset_time_elapsed_milliseconds < kResetTimeoutMilliseconds ) {
80
+ return std::string ();
81
+ }
82
+ break ;
83
+ }
84
+ instance_id_ = current_instance_id;
85
+ return current_instance_id;
86
+ }
87
+
88
+ private:
89
+ // Get the time elapsed in milliseconds since reset was requested.
90
+ uint64_t GetResetTimeElapsedMilliseconds () const {
91
+ return firebase::internal::GetTimestampEpoch () - reset_timestamp_;
92
+ }
93
+
94
+ private:
95
+ Mutex mutex_;
96
+ // Reset attempt.
97
+ ResetState reset_state_;
98
+ // When a reset was last requested.
99
+ uint64_t reset_timestamp_;
100
+ // Instance ID before it was reset.
101
+ std::string instance_id_;
102
+
103
+ // Time to wait before trying to reset again.
104
+ static const uint64_t kResetRetryIntervalMilliseconds ;
105
+ // Time to wait before giving up on resetting the ID.
106
+ static const uint64_t kResetTimeoutMilliseconds ;
107
+ };
108
+
32
109
DEFINE_FIREBASE_VERSION_STRING (FirebaseAnalytics);
33
110
111
+ const uint64_t AnalyticsDataResetter::kResetRetryIntervalMilliseconds = 1000 ;
112
+ const uint64_t AnalyticsDataResetter::kResetTimeoutMilliseconds = 5000 ;
113
+
34
114
static const double kMillisecondsPerSecond = 1000.0 ;
115
+ static Mutex g_mutex; // NOLINT
35
116
static bool g_initialized = false ;
117
+ static AnalyticsDataResetter *g_resetter = nullptr ;
36
118
37
119
// Initialize the API.
38
120
void Initialize (const ::firebase::App& app) {
121
+ MutexLock lock (g_mutex);
39
122
g_initialized = true ;
123
+ g_resetter = new AnalyticsDataResetter ();
40
124
internal::RegisterTerminateOnDefaultAppDestroy ();
41
125
internal::FutureData::Create ();
42
126
}
@@ -50,8 +134,11 @@ void Initialize(const ::firebase::App& app) {
50
134
51
135
// Terminate the API.
52
136
void Terminate () {
137
+ MutexLock lock (g_mutex);
53
138
internal::FutureData::Destroy ();
54
139
internal::UnregisterTerminateOnDefaultAppDestroy ();
140
+ delete g_resetter;
141
+ g_resetter = nullptr ;
55
142
g_initialized = false ;
56
143
}
57
144
@@ -169,22 +256,40 @@ void SetCurrentScreen(const char* screen_name, const char* screen_class) {
169
256
}
170
257
171
258
void ResetAnalyticsData () {
259
+ MutexLock lock (g_mutex);
172
260
FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
173
- [FIRAnalytics resetAnalyticsData ] ;
261
+ g_resetter-> Reset () ;
174
262
}
175
263
176
264
Future<std::string> GetAnalyticsInstanceId () {
265
+ MutexLock lock (g_mutex);
177
266
FIREBASE_ASSERT_RETURN (Future<std::string>(), internal::IsInitialized ());
178
267
auto * api = internal::FutureData::Get ()->api ();
179
268
const auto future_handle = api->SafeAlloc <std::string>(
180
269
internal::kAnalyticsFnGetAnalyticsInstanceId );
181
- api->CompleteWithResult (
182
- future_handle, 0 , " " ,
183
- util::StringFromNSString ([FIRAnalytics appInstanceID ]));
270
+ static int kPollTimeMs = 100 ;
271
+ Thread get_id_thread ([](SafeFutureHandle<std::string>* handle) {
272
+ for ( ; ; ) {
273
+ {
274
+ MutexLock lock (g_mutex);
275
+ if (!internal::IsInitialized ()) break ;
276
+ std::string instance_id = g_resetter->GetInstanceId ();
277
+ if (!instance_id.empty ()) {
278
+ internal::FutureData::Get ()->api ()->CompleteWithResult (
279
+ *handle, 0 , " " , instance_id);
280
+ break ;
281
+ }
282
+ }
283
+ firebase::internal::Sleep (kPollTimeMs );
284
+ }
285
+ delete handle;
286
+ }, new SafeFutureHandle<std::string>(future_handle));
287
+ get_id_thread.Detach ();
184
288
return Future<std::string>(api, future_handle.get ());
185
289
}
186
290
187
291
Future<std::string> GetAnalyticsInstanceIdLastResult () {
292
+ MutexLock lock (g_mutex);
188
293
FIREBASE_ASSERT_RETURN (Future<std::string>(), internal::IsInitialized ());
189
294
return static_cast <const Future<std::string>&>(
190
295
internal::FutureData::Get ()->api ()->LastResult (
0 commit comments