2424#include " app/src/include/firebase/version.h"
2525#include " app/src/assert.h"
2626#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"
2731#include " app/src/util_ios.h"
2832
2933namespace firebase {
3034namespace analytics {
3135
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+
32109DEFINE_FIREBASE_VERSION_STRING (FirebaseAnalytics);
33110
111+ const uint64_t AnalyticsDataResetter::kResetRetryIntervalMilliseconds = 1000 ;
112+ const uint64_t AnalyticsDataResetter::kResetTimeoutMilliseconds = 5000 ;
113+
34114static const double kMillisecondsPerSecond = 1000.0 ;
115+ static Mutex g_mutex; // NOLINT
35116static bool g_initialized = false ;
117+ static AnalyticsDataResetter *g_resetter = nullptr ;
36118
37119// Initialize the API.
38120void Initialize (const ::firebase::App& app) {
121+ MutexLock lock (g_mutex);
39122 g_initialized = true ;
123+ g_resetter = new AnalyticsDataResetter ();
40124 internal::RegisterTerminateOnDefaultAppDestroy ();
41125 internal::FutureData::Create ();
42126}
@@ -50,8 +134,11 @@ void Initialize(const ::firebase::App& app) {
50134
51135// Terminate the API.
52136void Terminate () {
137+ MutexLock lock (g_mutex);
53138 internal::FutureData::Destroy ();
54139 internal::UnregisterTerminateOnDefaultAppDestroy ();
140+ delete g_resetter;
141+ g_resetter = nullptr ;
55142 g_initialized = false ;
56143}
57144
@@ -169,22 +256,40 @@ void SetCurrentScreen(const char* screen_name, const char* screen_class) {
169256}
170257
171258void ResetAnalyticsData () {
259+ MutexLock lock (g_mutex);
172260 FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
173- [FIRAnalytics resetAnalyticsData ] ;
261+ g_resetter-> Reset () ;
174262}
175263
176264Future<std::string> GetAnalyticsInstanceId () {
265+ MutexLock lock (g_mutex);
177266 FIREBASE_ASSERT_RETURN (Future<std::string>(), internal::IsInitialized ());
178267 auto * api = internal::FutureData::Get ()->api ();
179268 const auto future_handle = api->SafeAlloc <std::string>(
180269 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 ();
184288 return Future<std::string>(api, future_handle.get ());
185289}
186290
187291Future<std::string> GetAnalyticsInstanceIdLastResult () {
292+ MutexLock lock (g_mutex);
188293 FIREBASE_ASSERT_RETURN (Future<std::string>(), internal::IsInitialized ());
189294 return static_cast <const Future<std::string>&>(
190295 internal::FutureData::Get ()->api ()->LastResult (
0 commit comments