Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ __pycache__/

### IDE generated files
.vscode/
**/.vs/

# Unencrypted secret files
google-services.json
Expand Down
2 changes: 1 addition & 1 deletion analytics/generate_windows_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
)
parser.add_argument(
"--windows_dll",
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/analytics_win.dll"),
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/google_analytics.dll"),
help="Path to the DLL file to calculate a hash."
)
parser.add_argument(
Expand Down
2 changes: 1 addition & 1 deletion analytics/integration_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ else()
)
elseif(MSVC)
set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32)
set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/analytics_win.dll")
set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/google_analytics.dll")

# For Windows, check if the Analytics DLL exists, and copy it in if so.
if (EXISTS "${ANALYTICS_WINDOWS_DLL}")
Expand Down
8 changes: 8 additions & 0 deletions analytics/integration_test/src/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,14 @@ TEST_F(FirebaseAnalyticsTest, TestLogEvents) {
"spoon_welders");
}

TEST_F(FirebaseAnalyticsTest, TestNotifyAppLifecycleChange) {
// Can't confirm that these do anything but just run them all to ensure the
// app doesn't crash.
firebase::analytics::NotifyAppLifecycleChange(firebase::analytics::kUnknown);
firebase::analytics::NotifyAppLifecycleChange(
firebase::analytics::kTermination);
}

TEST_F(FirebaseAnalyticsTest, TestLogEventWithMultipleParameters) {
const firebase::analytics::Parameter kLevelUpParameters[] = {
firebase::analytics::Parameter(firebase::analytics::kParameterLevel, 5),
Expand Down
13 changes: 12 additions & 1 deletion analytics/src/analytics_desktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace firebase {
namespace analytics {

#if defined(_WIN32)
#define ANALYTICS_DLL_FILENAME L"analytics_win.dll"
#define ANALYTICS_DLL_FILENAME L"google_analytics.dll"

static HMODULE g_analytics_module = 0;
#endif // defined(_WIN32)
Expand Down Expand Up @@ -134,6 +134,10 @@ bool IsInitialized() { return g_initialized; }
void Terminate() {
#if defined(_WIN32)
if (g_analytics_module) {
// Make sure to notify the SDK that the analytics is being terminated to
// upload any pending data.
NotifyAppLifecycleChange(AppLifecycleState::kTermination);

FirebaseAnalytics_UnloadDynamicFunctions();
FreeLibrary(g_analytics_module);
g_analytics_module = 0;
Expand Down Expand Up @@ -381,6 +385,13 @@ void ResetAnalyticsData() {
g_fake_instance_id++;
}

// Notify the Analytics SDK about the current state of the app's lifecycle.
void NotifyAppLifecycleChange(AppLifecycleState state) {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
GoogleAnalytics_NotifyAppLifecycleChange(
static_cast<GoogleAnalytics_AppLifecycleState>(state));
}

// Overloaded versions of LogEvent for convenience.

void LogEvent(const char* name) {
Expand Down
101 changes: 63 additions & 38 deletions analytics/src/analytics_desktop_dynamic.c

Large diffs are not rendered by default.

66 changes: 56 additions & 10 deletions analytics/src/analytics_desktop_dynamic.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ typedef struct GoogleAnalytics_Reserved_Opaque GoogleAnalytics_Reserved;
* using GoogleAnalytics_Options_Create(), initialization will fail, and the
* caller will be responsible for destroying the options.
*/
ANALYTICS_API typedef struct {
typedef struct ANALYTICS_API GoogleAnalytics_Options {
/**
* @brief The unique identifier for the Firebase app across all of Firebase
* with a platform-specific format. This is a required field, can not be null
Expand Down Expand Up @@ -68,12 +68,54 @@ ANALYTICS_API typedef struct {
*/
bool analytics_collection_enabled_at_first_launch;

/**
* @brief An optional path to a folder where the SDK can store its data.
* If not provided, the SDK will store its data in the same folder as the
* executable.
*
* The path must pre-exist and the app has read and write access to it.
*/
const char* app_data_directory;

/**
* @brief Reserved for internal use by the SDK.
*/
GoogleAnalytics_Reserved* reserved;
} GoogleAnalytics_Options;

/**
* @brief The state of an app in its lifecycle.
*/
typedef enum GoogleAnalytics_AppLifecycleState {
/**
* @brief This is an invalid state that is used to capture unininitialized
* values.
*/
GoogleAnalytics_AppLifecycleState_kUnknown = 0,
/**
* @brief The app is about to be terminated.
*/
GoogleAnalytics_AppLifecycleState_kTermination = 1,
} GoogleAnalytics_AppLifecycleState;

/**
* @brief The log level of a log message.
*/
typedef enum GoogleAnalytics_LogLevel {
kDebug,
kInfo,
kWarning,
kError,
} GoogleAnalytics_LogLevel;

/**
* @brief Function pointer type for a log callback.
*
* @param[in] message The log message string.
*/
typedef void (*GoogleAnalytics_LogCallback)(GoogleAnalytics_LogLevel log_level,
const char* message);

/**
* @brief Creates an instance of GoogleAnalytics_Options with default values.
*
Expand Down Expand Up @@ -149,25 +191,27 @@ extern "C" {
extern GoogleAnalytics_Options* (*ptr_GoogleAnalytics_Options_Create)();
extern void (*ptr_GoogleAnalytics_Options_Destroy)(GoogleAnalytics_Options* options);
extern GoogleAnalytics_Item* (*ptr_GoogleAnalytics_Item_Create)();
extern void (*ptr_GoogleAnalytics_Item_InsertInt)(GoogleAnalytics_Item* item, const char* key, int64_t value);
extern void (*ptr_GoogleAnalytics_Item_InsertDouble)(GoogleAnalytics_Item* item, const char* key, double value);
extern void (*ptr_GoogleAnalytics_Item_InsertString)(GoogleAnalytics_Item* item, const char* key, const char* value);
extern bool (*ptr_GoogleAnalytics_Item_InsertInt)(GoogleAnalytics_Item* item, const char* key, int64_t value);
extern bool (*ptr_GoogleAnalytics_Item_InsertDouble)(GoogleAnalytics_Item* item, const char* key, double value);
extern bool (*ptr_GoogleAnalytics_Item_InsertString)(GoogleAnalytics_Item* item, const char* key, const char* value);
extern void (*ptr_GoogleAnalytics_Item_Destroy)(GoogleAnalytics_Item* item);
extern GoogleAnalytics_ItemVector* (*ptr_GoogleAnalytics_ItemVector_Create)();
extern void (*ptr_GoogleAnalytics_ItemVector_InsertItem)(GoogleAnalytics_ItemVector* item_vector, GoogleAnalytics_Item* item);
extern bool (*ptr_GoogleAnalytics_ItemVector_InsertItem)(GoogleAnalytics_ItemVector* item_vector, GoogleAnalytics_Item* item);
extern void (*ptr_GoogleAnalytics_ItemVector_Destroy)(GoogleAnalytics_ItemVector* item_vector);
extern GoogleAnalytics_EventParameters* (*ptr_GoogleAnalytics_EventParameters_Create)();
extern void (*ptr_GoogleAnalytics_EventParameters_InsertInt)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, int64_t value);
extern void (*ptr_GoogleAnalytics_EventParameters_InsertDouble)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, double value);
extern void (*ptr_GoogleAnalytics_EventParameters_InsertString)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, const char* value);
extern void (*ptr_GoogleAnalytics_EventParameters_InsertItemVector)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, GoogleAnalytics_ItemVector* value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertInt)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, int64_t value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertDouble)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, double value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertString)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, const char* value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertItemVector)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, GoogleAnalytics_ItemVector* value);
extern void (*ptr_GoogleAnalytics_EventParameters_Destroy)(GoogleAnalytics_EventParameters* event_parameter_map);
extern bool (*ptr_GoogleAnalytics_Initialize)(const GoogleAnalytics_Options* options);
extern bool (*ptr_GoogleAnalytics_Initialize)(GoogleAnalytics_Options* options);
extern void (*ptr_GoogleAnalytics_LogEvent)(const char* name, GoogleAnalytics_EventParameters* parameters);
extern void (*ptr_GoogleAnalytics_SetUserProperty)(const char* name, const char* value);
extern void (*ptr_GoogleAnalytics_SetUserId)(const char* user_id);
extern void (*ptr_GoogleAnalytics_ResetAnalyticsData)();
extern void (*ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled)(bool enabled);
extern void (*ptr_GoogleAnalytics_SetLogCallback)(GoogleAnalytics_LogCallback callback);
extern void (*ptr_GoogleAnalytics_NotifyAppLifecycleChange)(GoogleAnalytics_AppLifecycleState state);

#define GoogleAnalytics_Options_Create ptr_GoogleAnalytics_Options_Create
#define GoogleAnalytics_Options_Destroy ptr_GoogleAnalytics_Options_Destroy
Expand All @@ -191,6 +235,8 @@ extern void (*ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled)(bool enabled);
#define GoogleAnalytics_SetUserId ptr_GoogleAnalytics_SetUserId
#define GoogleAnalytics_ResetAnalyticsData ptr_GoogleAnalytics_ResetAnalyticsData
#define GoogleAnalytics_SetAnalyticsCollectionEnabled ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled
#define GoogleAnalytics_SetLogCallback ptr_GoogleAnalytics_SetLogCallback
#define GoogleAnalytics_NotifyAppLifecycleChange ptr_GoogleAnalytics_NotifyAppLifecycleChange
// clang-format on

// Number of Google Analytics functions expected to be loaded from the DLL.
Expand Down
21 changes: 21 additions & 0 deletions analytics/src/include/firebase/analytics.h
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,27 @@ void SetSessionTimeoutDuration(int64_t milliseconds);
/// instance id.
void ResetAnalyticsData();

/// @brief The state of an app in its lifecycle.
///
/// kUnknown is an invalid state that is used to capture uninitialized values.
/// kTermination is used to indicate that the app is about to be terminated.
enum AppLifecycleState { kUnknown = 0, kTermination };

/// @brief Notifies the current state of the app's lifecycle.
///
/// This method is used to notify the Analytics SDK about the current state of
/// the app's lifecycle. The Analytics SDK will use this information to log
/// events, update user properties, upload data, etc.
///
/// kTermination is used to indicate that the app is about to be terminated.
/// The caller will be blocked until all pending data is uploaded or an error
/// occurs. The caller must ensure the OS does not terminate background threads
/// before the call returns.
///
/// @param[in] state The current state of the app's lifecycle.

void NotifyAppLifecycleChange(AppLifecycleState state);

/// Get the instance ID from the analytics service.
///
/// @note This is *not* the same ID as the ID returned by
Expand Down
Binary file removed analytics/windows/analytics_win.dll
Binary file not shown.
Binary file added analytics/windows/google_analytics.dll
Binary file not shown.
141 changes: 141 additions & 0 deletions analytics/windows/include/public/analytics.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ANALYTICS_MOBILE_CONSOLE_MEASUREMENT_PUBLIC_ANALYTICS_H_
#define ANALYTICS_MOBILE_CONSOLE_MEASUREMENT_PUBLIC_ANALYTICS_H_

#include <cstdint>
#include <functional>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -33,6 +47,41 @@ class Analytics {
std::variant<int64_t, double, std::string, ItemVector>;
using EventParameters = std::unordered_map<std::string, EventParameterValue>;

/**
* @brief The state of an app in its lifecycle.
*/
enum AppLifecycleState {
/**
* @brief This is an invalid state that is used to capture unininitialized
* values.
*/
kUnknown,
/**
* @brief The app is about to be terminated.
*/
kTermination,
};

/**
* @brief The log level of the message logged by the SDK.
*/
enum LogLevel {
kDebug,
kInfo,
kWarning,
kError,
};

/**
* @brief The callback type for logging messages from the SDK.
*
* The callback is invoked whenever the SDK logs a message.
*
* @param[in] log_level The log level of the message.
* @param[in] message The message logged by the SDK.
*/
using LogCallback = std::function<void(LogLevel, const std::string&)>;

/**
* @brief Options for initializing the Analytics SDK.
*/
Expand Down Expand Up @@ -63,6 +112,15 @@ class Analytics {
* point.
*/
bool analytics_collection_enabled_at_first_launch = true;

/**
* @brief An optional path to a folder where the SDK can store its data.
* If not provided, the SDK will store its data in the same folder as the
* executable.
*
* The path must pre-exist and the app has read and write access to it.
*/
std::optional<std::string> app_data_directory;
};

/**
Expand Down Expand Up @@ -96,6 +154,10 @@ class Analytics {
google_analytics_options->package_name = options.package_name.c_str();
google_analytics_options->analytics_collection_enabled_at_first_launch =
options.analytics_collection_enabled_at_first_launch;
google_analytics_options->app_data_directory =
options.app_data_directory.value_or("").empty()
? nullptr
: options.app_data_directory.value().c_str();
return GoogleAnalytics_Initialize(google_analytics_options);
}

Expand Down Expand Up @@ -269,8 +331,87 @@ class Analytics {
GoogleAnalytics_SetAnalyticsCollectionEnabled(enabled);
}

/**
* @brief Allows the passing of a callback to be used when the SDK logs any
* messages regarding its behavior. The callback must be thread-safe.
*
* @param[in] callback The callback to use. Must be thread-safe.
*/
void SetLogCallback(LogCallback callback) {
{
std::lock_guard<std::mutex> lock(mutex_);
current_callback_ = callback;
}

if (!callback) {
GoogleAnalytics_SetLogCallback(nullptr);
return;
}

GoogleAnalytics_SetLogCallback(
[](GoogleAnalytics_LogLevel log_level, const char* message) {
LogLevel cpp_log_level;
switch (log_level) {
case GoogleAnalytics_LogLevel::kDebug:
cpp_log_level = LogLevel::kDebug;
break;
case GoogleAnalytics_LogLevel::kInfo:
cpp_log_level = LogLevel::kInfo;
break;
case GoogleAnalytics_LogLevel::kWarning:
cpp_log_level = LogLevel::kWarning;
break;
case GoogleAnalytics_LogLevel::kError:
cpp_log_level = LogLevel::kError;
break;
default:
cpp_log_level = LogLevel::kInfo;
}
LogCallback local_callback;
Analytics& self = Analytics::GetInstance();
{
std::lock_guard<std::mutex> lock(self.mutex_);
local_callback = self.current_callback_;
}
if (local_callback) {
local_callback(cpp_log_level, std::string(message));
}
});
}

/**
* @brief Notifies the current state of the app's lifecycle.
*
* This method is used to notify the Analytics SDK about the current state of
* the app's lifecycle. The Analytics SDK will use this information to log
* events, update user properties, upload data, etc.
*
* kTermination is used to indicate that the app is about to be terminated.
* The caller will be blocked until all pending data is uploaded or an error
* occurs. The caller must ensure the OS does not terminate background threads
* before the call returns.
*
* @param[in] state The current state of the app's lifecycle.
*/
void NotifyAppLifecycleChange(AppLifecycleState state) {
GoogleAnalytics_AppLifecycleState c_state;
switch (state) {
case AppLifecycleState::kTermination:
c_state = GoogleAnalytics_AppLifecycleState_kTermination;
break;
case AppLifecycleState::kUnknown:
default:
c_state = GoogleAnalytics_AppLifecycleState_kUnknown;
break;
}
GoogleAnalytics_NotifyAppLifecycleChange(c_state);
}

private:
Analytics() = default;

std::mutex mutex_;
LogCallback current_callback_;
};

} // namespace google::analytics
Expand Down
Loading
Loading