Skip to content

Commit 7e10493

Browse files
john-mikhailgemini-code-assist[bot]firebase-workflow-trigger[bot]firebase-workflow-trigger-bota-maurice
authored
Analytics windows dll update (#1791)
* Update to use the latest analytics dll and source headers to account for latest changes. Add latest google_analytics.dll and corresponding public header files. Also added the implementations for SetLogCallback and NotifyAppLifecycleChange. * Update analytics.h Add missing header file. * Added integration tests Added integration test and removed the ability to set logging callback for now. Will add in the future. * Update analytics/src/include/firebase/analytics.h Fix typo Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Fix formatting * Fix formatting * Format fixes * Update mobile dependencies - Fri Oct 10 2025 (#1794) * Update mobile dependencies - Fri Oct 10 2025 ### Android - com.google.android.gms.play_services_base → 18.9.0 - com.google.firebase.firebase_bom → 34.4.0 ### iOS - Firebase/Analytics → 12.4.0 - Firebase/AppCheck → 12.4.0 - Firebase/Auth → 12.4.0 - Firebase/Core → 12.4.0 - Firebase/CoreOnly → 12.4.0 - Firebase/Crashlytics → 12.4.0 - Firebase/Database → 12.4.0 - Firebase/Firestore → 12.4.0 - Firebase/Functions → 12.4.0 - Firebase/Installations → 12.4.0 - Firebase/Messaging → 12.4.0 - Firebase/RemoteConfig → 12.4.0 - Firebase/Storage → 12.4.0 - FirebaseFirestore → 12.4.0 > Created by [Update Android and iOS dependencies workflow](https://github.com/firebase/firebase-cpp-sdk/actions/runs/18415569893). * Update readme.md * Update cpp_sdk_version.json --------- Co-authored-by: firebase-workflow-trigger-bot <[email protected]> Co-authored-by: a-maurice <[email protected]> * Update the python version used by GHA runners (#1797) * Feat: add UseEmulator to storage api (#1795) * Feat: add UseEmulator to storage api * Fix formating * Update storage/src/desktop/storage_desktop.h Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update release_build_files/readme.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Fix the storage util as storagePath now needs storageinternal ref To get the url to show up correctly the storage path now depends upon the storage internal to query for the host and port * Change the Release Notes and change the UseEmulator to give errors --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Feat: add in no ops for NotifyApplifecyclechange * fix formatting * fix the wrong param type * fixup the wording for NotifyApplifecyclechange to be windows only * feat: add in the Setlogcallback function for desktop analytics * Remove double implementation of the loglevel * Fix up the formatting and the enum in tc * format fix up and make test win only * Add in threading considerations for the callback test * Add in future header to the integration tests * Add in timeout to the test * Fix formatting and comments * Up the timeout to see if that helps to have the callback fire * Try to get an error from the underlying analytics dll * Trying to trigger the logger with an lifecycle change event * Make the wrapper function explicitly a c extern Also make sure nothing strange is happening with concurrency * fix spelling of wrapper * Try to increase the logging verbosity for a pass in actions * Makesure the DLL is loaded * Revert "Makesure the DLL is loaded" This reverts commit 063a08e. * Revert "Try to increase the logging verbosity for a pass in actions" This reverts commit 872764d. * Fix up before review * Fix formatting * Explain the new test * fixup from comments --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: firebase-workflow-trigger[bot] <80733318+firebase-workflow-trigger[bot]@users.noreply.github.com> Co-authored-by: firebase-workflow-trigger-bot <[email protected]> Co-authored-by: a-maurice <[email protected]> Co-authored-by: Austin Benoit <[email protected]>
1 parent 82607ff commit 7e10493

18 files changed

+566
-70
lines changed

.github/workflows/integration_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ jobs:
360360
if: startsWith(matrix.os, 'windows')
361361
shell: bash
362362
run: |
363-
find "testapps-desktop-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.ssl_variant }}" -name integration_test.exe -exec dirname {} \; | while read dir; do cp analytics/windows/analytics_win.dll "$dir/"; done
363+
find "testapps-desktop-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.ssl_variant }}" -name integration_test.exe -exec dirname {} \; | while read dir; do cp analytics/windows/google_analytics.dll "$dir/"; done
364364
- name: Upload Desktop Cmake
365365
uses: actions/upload-artifact@v4
366366
if: ${{ !cancelled() }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ __pycache__/
1717

1818
### IDE generated files
1919
.vscode/
20+
**/.vs/
2021

2122
# Unencrypted secret files
2223
google-services.json

analytics/generate_windows_stubs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
262262
)
263263
parser.add_argument(
264264
"--windows_dll",
265-
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/analytics_win.dll"),
265+
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/google_analytics.dll"),
266266
help="Path to the DLL file to calculate a hash."
267267
)
268268
parser.add_argument(

analytics/integration_test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ else()
211211
)
212212
elseif(MSVC)
213213
set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32)
214-
set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/analytics_win.dll")
214+
set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/google_analytics.dll")
215215

216216
# For Windows, check if the Analytics DLL exists, and copy it in if so.
217217
if (EXISTS "${ANALYTICS_WINDOWS_DLL}")

analytics/integration_test/src/integration_test.cc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
#include <inttypes.h>
1616

1717
#include <algorithm>
18+
#include <chrono>
1819
#include <cstdio>
1920
#include <cstdlib>
2021
#include <cstring>
2122
#include <ctime>
23+
#include <future>
2224

2325
#include "app_framework.h" // NOLINT
2426
#include "firebase/analytics.h"
@@ -242,6 +244,36 @@ TEST_F(FirebaseAnalyticsTest, TestSetProperties) {
242244
InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber(hashed_phone);
243245
}
244246

247+
#if defined(_WIN32)
248+
// The windows analytics DLL enables users to // set a callback function that is
249+
// triggered when the underlying DLL logs something.
250+
TEST_F(FirebaseAnalyticsTest, TestSetLogCallback) {
251+
std::promise<void> finishedPromise;
252+
std::future<void> finished = finishedPromise.get_future();
253+
bool log_callback_called = false;
254+
255+
firebase::analytics::SetLogCallback(
256+
[&](firebase::LogLevel log_level, const char* message) {
257+
log_callback_called = true;
258+
finishedPromise.set_value();
259+
});
260+
261+
// Sending a NotifyAppLifecycleChange with kUnknown will trigger a logging
262+
// callback
263+
firebase::analytics::NotifyAppLifecycleChange(firebase::analytics::kUnknown);
264+
265+
if (finished.wait_for(std::chrono::seconds(60)) ==
266+
std::future_status::timeout) {
267+
ADD_FAILURE() << "Timed out waiting for log callback";
268+
} else {
269+
finished.get();
270+
}
271+
272+
EXPECT_TRUE(log_callback_called);
273+
firebase::analytics::SetLogCallback(nullptr);
274+
}
275+
#endif // defined(_WIN32)
276+
245277
TEST_F(FirebaseAnalyticsTest, TestLogEvents) {
246278
// Log an event with no parameters.
247279
firebase::analytics::LogEvent(firebase::analytics::kEventLogin);
@@ -259,6 +291,14 @@ TEST_F(FirebaseAnalyticsTest, TestLogEvents) {
259291
"spoon_welders");
260292
}
261293

294+
TEST_F(FirebaseAnalyticsTest, TestNotifyAppLifecycleChange) {
295+
// Can't confirm that these do anything but just run them all to ensure the
296+
// app doesn't crash.
297+
firebase::analytics::NotifyAppLifecycleChange(firebase::analytics::kUnknown);
298+
firebase::analytics::NotifyAppLifecycleChange(
299+
firebase::analytics::kTermination);
300+
}
301+
262302
TEST_F(FirebaseAnalyticsTest, TestLogEventWithMultipleParameters) {
263303
const firebase::analytics::Parameter kLevelUpParameters[] = {
264304
firebase::analytics::Parameter(firebase::analytics::kParameterLevel, 5),

analytics/src/analytics_android.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,12 @@ void ResetAnalyticsData() {
666666
util::CheckAndClearJniExceptions(env);
667667
}
668668

669+
// NO-OP in Android and iOS. Only used in Windows.
670+
void SetLogCallback(const LogCallback&) {}
671+
672+
// NO-OP in Android and iOS. Only used in Windows.
673+
void NotifyAppLifecycleChange(AppLifecycleState) {}
674+
669675
Future<std::string> GetAnalyticsInstanceId() {
670676
FIREBASE_ASSERT_RETURN(GetAnalyticsInstanceIdLastResult(),
671677
internal::IsInitialized());

analytics/src/analytics_desktop.cc

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <future>
1516
#include <map>
17+
#include <mutex>
1618
#include <sstream>
1719
#include <string>
1820
#include <vector>
@@ -25,6 +27,7 @@
2527
#include "app/src/include/firebase/future.h"
2628
#include "app/src/include/firebase/variant.h"
2729
#include "app/src/log.h"
30+
#include "firebase/log.h"
2831

2932
#if defined(_WIN32)
3033
#include <windows.h>
@@ -36,14 +39,16 @@ namespace firebase {
3639
namespace analytics {
3740

3841
#if defined(_WIN32)
39-
#define ANALYTICS_DLL_FILENAME L"analytics_win.dll"
42+
#define ANALYTICS_DLL_FILENAME L"google_analytics.dll"
4043

4144
static HMODULE g_analytics_module = 0;
4245
#endif // defined(_WIN32)
4346

4447
// Future data for analytics.
4548
// This is initialized in `Initialize()` and cleaned up in `Terminate()`.
4649
static bool g_initialized = false;
50+
static LogCallback g_log_callback;
51+
static std::mutex g_log_callback_mutex;
4752
static int g_fake_instance_id = 0;
4853
static bool g_analytics_collection_enabled = true;
4954
static std::string g_app_id;
@@ -134,6 +139,10 @@ bool IsInitialized() { return g_initialized; }
134139
void Terminate() {
135140
#if defined(_WIN32)
136141
if (g_analytics_module) {
142+
// Make sure to notify the SDK that the analytics is being terminated to
143+
// upload any pending data.
144+
NotifyAppLifecycleChange(AppLifecycleState::kTermination);
145+
137146
FirebaseAnalytics_UnloadDynamicFunctions();
138147
FreeLibrary(g_analytics_module);
139148
g_analytics_module = 0;
@@ -386,6 +395,61 @@ void ResetAnalyticsData() {
386395
g_fake_instance_id++;
387396
}
388397

398+
LogLevel ConvertAnalyticsLogLevelToFirebaseLogLevel(
399+
GoogleAnalytics_LogLevel log_level) {
400+
switch (log_level) {
401+
case kDebug:
402+
return kLogLevelDebug;
403+
case kInfo:
404+
return kLogLevelInfo;
405+
case kWarning:
406+
return kLogLevelWarning;
407+
case kError:
408+
return kLogLevelError;
409+
default:
410+
return kLogLevelInfo;
411+
}
412+
}
413+
414+
void GoogleAnalyticsWrapperLogCallback(GoogleAnalytics_LogLevel log_level,
415+
const char* message) {
416+
LogCallback callback_to_call;
417+
418+
{
419+
std::lock_guard<std::mutex> lock(g_log_callback_mutex);
420+
callback_to_call = g_log_callback;
421+
}
422+
423+
if (callback_to_call) {
424+
LogLevel firebase_log_level =
425+
ConvertAnalyticsLogLevelToFirebaseLogLevel(log_level);
426+
callback_to_call(firebase_log_level, message);
427+
}
428+
}
429+
430+
// Allows the passing of a callback to be used when the SDK logs any
431+
// messages regarding its behavior. The callback must be thread-safe.
432+
void SetLogCallback(const LogCallback& callback) {
433+
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
434+
// The C API does not support user data, so we must use a global variable.
435+
{
436+
std::lock_guard<std::mutex> lock(g_log_callback_mutex);
437+
g_log_callback = callback;
438+
}
439+
if (callback) {
440+
GoogleAnalytics_SetLogCallback(GoogleAnalyticsWrapperLogCallback);
441+
} else {
442+
GoogleAnalytics_SetLogCallback(nullptr);
443+
}
444+
}
445+
446+
// Notify the Analytics SDK about the current state of the app's lifecycle.
447+
void NotifyAppLifecycleChange(AppLifecycleState state) {
448+
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
449+
GoogleAnalytics_NotifyAppLifecycleChange(
450+
static_cast<GoogleAnalytics_AppLifecycleState>(state));
451+
}
452+
389453
// Overloaded versions of LogEvent for convenience.
390454

391455
void LogEvent(const char* name) {

0 commit comments

Comments
 (0)