diff --git a/analytics/CMakeLists.txt b/analytics/CMakeLists.txt index 84166eb772..0b5603f871 100644 --- a/analytics/CMakeLists.txt +++ b/analytics/CMakeLists.txt @@ -74,9 +74,15 @@ set(android_SRCS set(ios_SRCS src/analytics_ios.mm) -# Source files used by the stub implementation. -set(stub_SRCS - src/analytics_stub.cc) +# Source files used by the desktop / stub implementation. +set(desktop_SRCS + src/analytics_desktop.cc + src/analytics_desktop_dynamic.c) + +if(WIN32) + # Add Windows-specific sources for desktop builds. + list(APPEND desktop_SRCS src/analytics_windows.cc) +endif() if(ANDROID) set(analytics_platform_SRCS @@ -86,7 +92,7 @@ elseif(IOS) "${ios_SRCS}") else() set(analytics_platform_SRCS - "${stub_SRCS}") + "${desktop_SRCS}") endif() add_library(firebase_analytics STATIC diff --git a/analytics/generate_windows_stubs.py b/analytics/generate_windows_stubs.py index 4ef2a76b64..cc89422395 100755 --- a/analytics/generate_windows_stubs.py +++ b/analytics/generate_windows_stubs.py @@ -16,12 +16,13 @@ """Generate stubs and function pointers for Windows SDK""" import argparse +import hashlib import os import re import sys HEADER_GUARD_PREFIX = "FIREBASE_ANALYTICS_SRC_WINDOWS_" -INCLUDE_PATH = "src/windows/" +INCLUDE_PATH = "src/" INCLUDE_PREFIX = "analytics/" + INCLUDE_PATH COPYRIGHT_NOTICE = """// Copyright 2025 Google LLC // @@ -39,17 +40,29 @@ """ -def generate_function_pointers(header_file_path, output_h_path, output_c_path): + +def hash_file(filename): + sha256_hash = hashlib.sha256() + with open(filename, "rb") as file: + while chunk := file.read(4096): + sha256_hash.update(chunk) + return sha256_hash.digest() + +def generate_function_pointers(dll_file_path, header_file_path, output_h_path, output_c_path): """ Parses a C header file to generate a self-contained header with typedefs, extern function pointer declarations, and a source file with stub functions, initialized pointers, and a dynamic loading function for Windows. Args: + header_file_path (str): The path to the DLL file. header_file_path (str): The path to the input C header file. output_h_path (str): The path for the generated C header output file. output_c_path (str): The path for the generated C source output file. """ + print(f"Reading DLL file: {dll_file_path}") + dll_hash = hash_file(dll_file_path) + print(f"Reading header file: {header_file_path}") try: with open(header_file_path, 'r', encoding='utf-8') as f: @@ -83,7 +96,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): return_type = match.group(1).strip() function_name = match.group(2).strip() params_str = match.group(3).strip() - + cleaned_params_for_decl = re.sub(r'\s+', ' ', params_str) if params_str else "" stub_name = f"Stub_{function_name}" @@ -94,7 +107,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): return_statement = f' return ({return_type})(&g_stub_memory);' else: # bool, int64_t, etc. return_statement = " return 1;" - + stub_function = ( f"// Stub for {function_name}\n" f"static {return_type} {stub_name}({params_str}) {{\n" @@ -111,7 +124,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): pointer_init = f"{return_type} (*ptr_{function_name})({cleaned_params_for_decl}) = &{stub_name};" pointer_initializations.append(pointer_init) - + function_details_for_loader.append((function_name, return_type, cleaned_params_for_decl)) print(f"Found {len(pointer_initializations)} functions. Generating output files...") @@ -124,12 +137,12 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): f.write(f"#ifndef {header_guard}\n") f.write(f"#define {header_guard}\n\n") f.write("#include // needed for bool type in pure C\n\n") - + f.write("// --- Copied from original header ---\n") f.write("\n".join(includes) + "\n\n") f.write("".join(typedefs)) f.write("// --- End of copied section ---\n\n") - + f.write("#ifdef __cplusplus\n") f.write('extern "C" {\n') f.write("#endif\n\n") @@ -139,15 +152,19 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): f.write("\n\n") f.write("\n".join(macro_definitions)) f.write("\n// clang-format on\n") - f.write("\n\n// --- Dynamic Loader Declaration for Windows ---\n") - f.write("#if defined(_WIN32)\n") - f.write('#include // For HMODULE\n') - f.write('// Load Google Analytics functions from the given DLL handle into function pointers.\n') - f.write(f'// Returns the number of functions successfully loaded (out of {len(function_details_for_loader)}).\n') - f.write("int FirebaseAnalytics_LoadAnalyticsFunctions(HMODULE dll_handle);\n\n") + f.write(f'\n// Number of Google Analytics functions expected to be loaded from the DLL.') + f.write('\nextern const int FirebaseAnalytics_DynamicFunctionCount;\n'); + f.write("\n// --- Dynamic Loader Declaration for Windows ---\n") + f.write("#if defined(_WIN32)\n\n") + f.write('#include \n') + f.write(f'\n// Google Analytics Windows DLL SHA256 hash, to be verified before loading.') + f.write(f'\nextern const unsigned char FirebaseAnalytics_WindowsDllHash[{len(dll_hash)}];\n\n'); + f.write('// Load Analytics functions from the given DLL handle into function pointers.\n') + f.write(f'// Returns the number of functions successfully loaded.\n') + f.write("int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle);\n\n") f.write('// Reset all function pointers back to stubs.\n') - f.write("void FirebaseAnalytics_UnloadAnalyticsFunctions(void);\n\n") - f.write("#endif // defined(_WIN32)\n") + f.write("void FirebaseAnalytics_UnloadDynamicFunctions(void);\n\n") + f.write("#endif // defined(_WIN32)\n") f.write("\n#ifdef __cplusplus\n") f.write("}\n") f.write("#endif\n\n") @@ -159,18 +176,26 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): with open(output_c_path, 'w', encoding='utf-8') as f: f.write(f"{COPYRIGHT_NOTICE}") f.write(f"// Generated from {os.path.basename(header_file_path)} by {os.path.basename(sys.argv[0])}\n\n") - f.write(f'#include "{INCLUDE_PREFIX}{os.path.basename(output_h_path)}"\n') + f.write(f'#include "{INCLUDE_PREFIX}{os.path.basename(output_h_path)}"\n\n') f.write('#include \n\n') - f.write("// clang-format off\n\n") f.write("static void* g_stub_memory = NULL;\n\n") - f.write("// --- Stub Function Definitions ---\n") + f.write("// clang-format off\n") + f.write(f'\n// Number of Google Analytics functions expected to be loaded from the DLL.') + f.write(f'\nconst int FirebaseAnalytics_DynamicFunctionCount = {len(function_details_for_loader)};\n\n'); + f.write("#if defined(_WIN32)\n") + f.write('// Google Analytics Windows DLL SHA256 hash, to be verified before loading.\n') + f.write('const unsigned char FirebaseAnalytics_WindowsDllHash[] = {\n ') + f.write(', '.join(["0x%02x" % s for s in dll_hash])) + f.write('\n};\n') + f.write("#endif // defined(_WIN32)\n") + f.write("\n// --- Stub Function Definitions ---\n") f.write("\n\n".join(stub_functions)) f.write("\n\n\n// --- Function Pointer Initializations ---\n") f.write("\n".join(pointer_initializations)) f.write("\n\n// --- Dynamic Loader Function for Windows ---\n") loader_lines = [ '#if defined(_WIN32)', - 'int FirebaseAnalytics_LoadAnalyticsFunctions(HMODULE dll_handle) {', + 'int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle) {', ' int count = 0;\n', ' if (!dll_handle) {', ' return count;', @@ -188,7 +213,7 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): loader_lines.extend(proc_check) loader_lines.append('\n return count;') loader_lines.append('}\n') - loader_lines.append('void FirebaseAnalytics_UnloadAnalyticsFunctions(void) {') + loader_lines.append('void FirebaseAnalytics_UnloadDynamicFunctions(void) {') for name, ret_type, params in function_details_for_loader: loader_lines.append(f' ptr_{name} = &Stub_{name};'); loader_lines.append('}\n') @@ -203,6 +228,12 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): parser = argparse.ArgumentParser( description="Generate C stubs and function pointers from a header file." ) + parser.add_argument( + "--windows_dll", + default = os.path.join(os.path.dirname(sys.argv[0]), "windows/analytics_win.dll"), + #required=True, + help="Path to the DLL file to calculate a hash." + ) parser.add_argument( "--windows_header", default = os.path.join(os.path.dirname(sys.argv[0]), "windows/include/public/c/analytics.h"), @@ -211,21 +242,21 @@ def generate_function_pointers(header_file_path, output_h_path, output_c_path): ) parser.add_argument( "--output_header", - default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_dynamic.h"), + default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.h"), #required=True, help="Path for the generated output header file." ) parser.add_argument( "--output_source", - default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_dynamic.c"), + default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.c"), #required=True, help="Path for the generated output source file." ) - args = parser.parse_args() - + generate_function_pointers( - args.windows_header, - args.output_header, + args.windows_dll, + args.windows_header, + args.output_header, args.output_source ) diff --git a/analytics/integration_test/CMakeLists.txt b/analytics/integration_test/CMakeLists.txt index 4824536107..3a91d28c84 100644 --- a/analytics/integration_test/CMakeLists.txt +++ b/analytics/integration_test/CMakeLists.txt @@ -211,6 +211,16 @@ else() ) elseif(MSVC) set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/analytics_win.dll") + + # For Windows, check if the Analytics DLL exists, and copy it in if so. + if (EXISTS "${ANALYTICS_WINDOWS_DLL}") + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${ANALYTICS_WINDOWS_DLL}" + $) + endif() else() set(ADDITIONAL_LIBS pthread) endif() diff --git a/analytics/src/analytics_desktop.cc b/analytics/src/analytics_desktop.cc new file mode 100644 index 0000000000..5e9669bf7b --- /dev/null +++ b/analytics/src/analytics_desktop.cc @@ -0,0 +1,495 @@ +// 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. + +#include +#include +#include +#include + +#include "analytics/src/analytics_common.h" +#include "analytics/src/analytics_desktop_dynamic.h" +#include "analytics/src/include/firebase/analytics.h" +#include "app/src/future_manager.h" // For FutureData +#include "app/src/include/firebase/app.h" +#include "app/src/include/firebase/future.h" +#include "app/src/include/firebase/variant.h" +#include "app/src/log.h" + +#if defined(_WIN32) +#include + +#include "analytics/src/analytics_windows.h" +#endif // defined(_WIN32) + +namespace firebase { +namespace analytics { + +#if defined(_WIN32) +#define ANALYTICS_DLL_FILENAME L"analytics_win.dll" + +static HMODULE g_analytics_module = 0; +#endif // defined(_WIN32) + +// Future data for analytics. +// This is initialized in `Initialize()` and cleaned up in `Terminate()`. +static bool g_initialized = false; +static int g_fake_instance_id = 0; + +// Initializes the Analytics desktop API. +// This function must be called before any other Analytics methods. +void Initialize(const App& app) { + // The 'app' parameter is not directly used by the underlying Google Analytics + // C API for Windows for global initialization. It's included for API + // consistency with other Firebase platforms. + (void)app; + + g_initialized = true; + internal::RegisterTerminateOnDefaultAppDestroy(); + internal::FutureData::Create(); + g_fake_instance_id = 0; + +#if defined(_WIN32) + if (!g_analytics_module) { + std::vector> allowed_hashes; + std::vector current_hash; + current_hash.assign(FirebaseAnalytics_WindowsDllHash, + FirebaseAnalytics_WindowsDllHash + + sizeof(FirebaseAnalytics_WindowsDllHash)); + allowed_hashes.push_back(current_hash); + + g_analytics_module = + firebase::analytics::internal::VerifyAndLoadAnalyticsLibrary( + ANALYTICS_DLL_FILENAME, allowed_hashes); + + if (g_analytics_module) { + int num_loaded = FirebaseAnalytics_LoadDynamicFunctions( + g_analytics_module); // Ensure g_analytics_module is used + if (num_loaded < FirebaseAnalytics_DynamicFunctionCount) { + LogWarning( + "Analytics: Failed to load functions from Google Analytics " + "module (%d out of %d loaded), reverting to stubs.", + num_loaded, FirebaseAnalytics_DynamicFunctionCount); + FirebaseAnalytics_UnloadDynamicFunctions(); + FreeLibrary(g_analytics_module); + g_analytics_module = 0; + } else { + LogInfo("Analytics: Loaded Google Analytics module."); + } + } + } +#endif +} + +namespace internal { + +// Determine whether the analytics module is initialized. +bool IsInitialized() { return g_initialized; } + +} // namespace internal + +// Terminates the Analytics desktop API. +// Call this function when Analytics is no longer needed to free up resources. +void Terminate() { +#if defined(_WIN32) + if (g_analytics_module) { + FirebaseAnalytics_UnloadDynamicFunctions(); + FreeLibrary(g_analytics_module); + g_analytics_module = 0; + } +#endif + + internal::FutureData::Destroy(); + internal::UnregisterTerminateOnDefaultAppDestroy(); + g_initialized = false; +} + +static void ConvertParametersToGAParams( + const Parameter* parameters, size_t number_of_parameters, + GoogleAnalytics_EventParameters* c_event_params) { + if (!parameters || number_of_parameters == 0 || !c_event_params) { + return; + } + + for (size_t i = 0; i < number_of_parameters; ++i) { + const Parameter& param = parameters[i]; + if (param.name == nullptr || param.name[0] == '\0') { + LogError("Analytics: Parameter name cannot be null or empty."); + continue; + } + + if (param.value.is_int64()) { + GoogleAnalytics_EventParameters_InsertInt(c_event_params, param.name, + param.value.int64_value()); + } else if (param.value.is_double()) { + GoogleAnalytics_EventParameters_InsertDouble(c_event_params, param.name, + param.value.double_value()); + } else if (param.value.is_string()) { + GoogleAnalytics_EventParameters_InsertString(c_event_params, param.name, + param.value.string_value()); + } else if (param.value.is_vector()) { + // Vector types for top-level event parameters are not supported on + // Desktop. Only specific complex types (like a map processed into an + // ItemVector) are handled. + LogError( + "Analytics: Parameter '%s' has type Vector, which is unsupported for " + "event parameters on Desktop. Skipping.", + param.name); + continue; // Skip this parameter + } else if (param.value.is_map()) { + // This block handles parameters that are maps. + // Each key-value pair from the input map is converted into a distinct + // GoogleAnalytics_Item. In each such GoogleAnalytics_Item, the original + // key from the map is used directly as the property key, and the original + // value (which must be a primitive) is set as the property's value. All + // these GoogleAnalytics_Items are then bundled into a single + // GoogleAnalytics_ItemVector, which is associated with the original + // parameter's name. + const std::map& user_map = + param.value.map(); + if (user_map.empty()) { + LogWarning("Analytics: Parameter '%s' is an empty map. Skipping.", + param.name); + continue; // Skip this parameter + } + + GoogleAnalytics_ItemVector* c_item_vector = + GoogleAnalytics_ItemVector_Create(); + if (!c_item_vector) { + LogError( + "Analytics: Failed to create ItemVector for map parameter '%s'.", + param.name); + continue; // Skip this parameter + } + + bool item_vector_populated = false; + for (const auto& entry : user_map) { + const firebase::Variant& key_variant = entry.first; + if (!key_variant.is_string()) { + LogError("Analytics: Non-string map key found. Skipping."); + continue; + } + const std::string& key_from_map = key_variant.mutable_string(); + const firebase::Variant& value_from_map = entry.second; + + GoogleAnalytics_Item* c_item = GoogleAnalytics_Item_Create(); + if (!c_item) { + LogError( + "Analytics: Failed to create Item for key '%s' in map parameter " + "'%s'.", + key_from_map.c_str(), param.name); + continue; // Skip this key-value pair, try next one in map + } + + bool successfully_set_property = false; + if (value_from_map.is_int64()) { + GoogleAnalytics_Item_InsertInt(c_item, key_from_map.c_str(), + value_from_map.int64_value()); + successfully_set_property = true; + } else if (value_from_map.is_double()) { + GoogleAnalytics_Item_InsertDouble(c_item, key_from_map.c_str(), + value_from_map.double_value()); + successfully_set_property = true; + } else if (value_from_map.is_string()) { + GoogleAnalytics_Item_InsertString(c_item, key_from_map.c_str(), + value_from_map.string_value()); + successfully_set_property = true; + } else { + LogWarning( + "Analytics: Value for key '%s' in map parameter '%s' has an " + "unsupported Variant type. This key-value pair will be skipped.", + key_from_map.c_str(), param.name); + // successfully_set_property remains false + } + + if (successfully_set_property) { + GoogleAnalytics_ItemVector_InsertItem(c_item_vector, c_item); + // c_item is now owned by c_item_vector + item_vector_populated = + true; // Mark that the vector has at least one item + } else { + // If no property was set (e.g., value type was unsupported), destroy + // the created c_item. + GoogleAnalytics_Item_Destroy(c_item); + } + } + + if (item_vector_populated) { + GoogleAnalytics_EventParameters_InsertItemVector( + c_event_params, param.name, c_item_vector); + // c_item_vector is now owned by c_event_params + } else { + // If no items were successfully created and added (e.g., all values in + // map were unsupported types) + GoogleAnalytics_ItemVector_Destroy(c_item_vector); + LogWarning( + "Analytics: Map parameter '%s' resulted in an empty ItemVector; no " + "valid key-value pairs found or all values had unsupported types. " + "This map parameter was skipped.", + param.name); + } + } else { + LogWarning("Analytics: Unsupported variant type for parameter '%s'.", + param.name); + } + } +} + +// Logs an event with the given name and parameters. +void LogEvent(const char* name, const Parameter* parameters, + size_t number_of_parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + + if (name == nullptr || name[0] == '\0') { + LogError("Analytics: Event name cannot be null or empty."); + return; + } + + GoogleAnalytics_EventParameters* c_event_params = nullptr; + if (parameters != nullptr && number_of_parameters > 0) { + c_event_params = GoogleAnalytics_EventParameters_Create(); + if (!c_event_params) { + LogError( + "Analytics: Failed to create event parameters map for event '%s'.", + name); + return; + } + ConvertParametersToGAParams(parameters, number_of_parameters, + c_event_params); + } + + GoogleAnalytics_LogEvent(name, c_event_params); + // GoogleAnalytics_LogEvent is expected to handle the lifecycle of + // c_event_params if non-null. +} + +// Sets a user property to the given value. +// +// Up to 25 user property names are supported. Once set, user property values +// persist throughout the app lifecycle and across sessions. +// +// @param[in] name The name of the user property to set. Should contain 1 to 24 +// alphanumeric characters or underscores, and must start with an alphabetic +// character. The "firebase_", "google_", and "ga_" prefixes are reserved and +// should not be used for user property names. Must be UTF-8 encoded. +// @param[in] value The value of the user property. Values can be up to 36 +// characters long. Setting the value to `nullptr` or an empty string will +// clear the user property. Must be UTF-8 encoded if not nullptr. +void SetUserProperty(const char* name, const char* property) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + + if (name == nullptr || name[0] == '\0') { + LogError("Analytics: User property name cannot be null or empty."); + return; + } + // The C API GoogleAnalytics_SetUserProperty allows value to be nullptr to + // remove the property. If value is an empty string, it might also be treated + // as clearing by some backends, or it might be an invalid value. The C API + // doc says: "Setting the value to `nullptr` remove the user property." For + // consistency, we pass it as is. + GoogleAnalytics_SetUserProperty(name, property); +} + +// Sets the user ID property. +// This feature must be used in accordance with Google's Privacy Policy. +// +// @param[in] user_id The user ID associated with the user of this app on this +// device. The user ID must be non-empty if not nullptr, and no more than 256 +// characters long, and UTF-8 encoded. Setting user_id to `nullptr` removes +// the user ID. +void SetUserId(const char* user_id) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + // The C API GoogleAnalytics_SetUserId allows user_id to be nullptr to clear + // the user ID. The C API documentation also mentions: "The user ID must be + // non-empty and no more than 256 characters long". We'll pass nullptr as is. + // If user_id is an empty string "", this might be an issue for the underlying + // C API or backend if it expects non-empty. However, the Firebase API + // typically allows passing "" to clear some fields, or it's treated as an + // invalid value. For SetUserId, `nullptr` is the standard clear mechanism. An + // empty string might be an invalid ID. For now, we are not adding extra + // validation for empty string beyond what C API does. Consider adding a check + // for empty string if Firebase spec requires it. e.g., if (user_id != nullptr + // && user_id[0] == '\0') { /* log error */ return; } + GoogleAnalytics_SetUserId(user_id); +} + +// Sets whether analytics collection is enabled for this app on this device. +// This setting is persisted across app sessions. By default it is enabled. +// +// @param[in] enabled A flag that enables or disables Analytics collection. +void SetAnalyticsCollectionEnabled(bool enabled) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + + GoogleAnalytics_SetAnalyticsCollectionEnabled(enabled); +} + +// Clears all analytics data for this app from the device and resets the app +// instance ID. +void ResetAnalyticsData() { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + + GoogleAnalytics_ResetAnalyticsData(); + g_fake_instance_id++; +} + +// Overloaded versions of LogEvent for convenience. + +void LogEvent(const char* name) { + LogEvent(name, static_cast(nullptr), 0); +} + +void LogEvent(const char* name, const char* parameter_name, + const char* parameter_value) { + if (parameter_name == nullptr) { + LogEvent(name, static_cast(nullptr), 0); + return; + } + Parameter param(parameter_name, parameter_value); + LogEvent(name, ¶m, 1); +} + +void LogEvent(const char* name, const char* parameter_name, + const double parameter_value) { + if (parameter_name == nullptr) { + LogEvent(name, static_cast(nullptr), 0); + return; + } + Parameter param(parameter_name, parameter_value); + LogEvent(name, ¶m, 1); +} + +void LogEvent(const char* name, const char* parameter_name, + const int64_t parameter_value) { + if (parameter_name == nullptr) { + LogEvent(name, static_cast(nullptr), 0); + return; + } + Parameter param(parameter_name, parameter_value); + LogEvent(name, ¶m, 1); +} + +void LogEvent(const char* name, const char* parameter_name, + const int parameter_value) { + if (parameter_name == nullptr) { + LogEvent(name, static_cast(nullptr), 0); + return; + } + Parameter param(parameter_name, static_cast(parameter_value)); + LogEvent(name, ¶m, 1); +} + +// --- Stub Implementations for Unsupported Features --- + +void SetConsent(const std::map& consent_settings) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + + // Not supported by the Windows C API. + (void)consent_settings; // Mark as unused + LogWarning( + "Analytics: SetConsent() is not supported and has no effect on Desktop."); +} + +void InitiateOnDeviceConversionMeasurementWithEmailAddress( + const char* email_address) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + (void)email_address; + LogWarning( + "Analytics: InitiateOnDeviceConversionMeasurementWithEmailAddress() is " + "not supported and has no effect on Desktop."); +} + +void InitiateOnDeviceConversionMeasurementWithPhoneNumber( + const char* phone_number) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + (void)phone_number; + LogWarning( + "Analytics: InitiateOnDeviceConversionMeasurementWithPhoneNumber() is " + "not supported and has no effect on Desktop."); +} + +void InitiateOnDeviceConversionMeasurementWithHashedEmailAddress( + std::vector hashed_email_address) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + (void)hashed_email_address; + LogWarning( + "Analytics: " + "InitiateOnDeviceConversionMeasurementWithHashedEmailAddress() is not " + "supported and has no effect on Desktop."); +} + +void InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber( + std::vector hashed_phone_number) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + (void)hashed_phone_number; + LogWarning( + "Analytics: InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber() " + "is not supported and has no effect on Desktop."); +} + +void SetSessionTimeoutDuration(int64_t milliseconds) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + (void)milliseconds; + LogWarning( + "Analytics: SetSessionTimeoutDuration() is not supported and has no " + "effect on Desktop."); +} + +Future GetAnalyticsInstanceId() { + FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); + auto* api = internal::FutureData::Get()->api(); + const auto future_handle = + api->SafeAlloc(internal::kAnalyticsFnGetAnalyticsInstanceId); + std::string instance_id = std::string("FakeAnalyticsInstanceId"); + { + std::stringstream ss; + ss << g_fake_instance_id; + instance_id += ss.str(); + } + api->CompleteWithResult(future_handle, 0, "", instance_id); + LogWarning( + "Analytics: GetAnalyticsInstanceId() is not supported on Desktop."); + return Future(api, future_handle.get()); +} + +Future GetAnalyticsInstanceIdLastResult() { + FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); + LogWarning( + "Analytics: GetAnalyticsInstanceIdLastResult() is not supported on " + "Desktop."); + return static_cast&>( + internal::FutureData::Get()->api()->LastResult( + internal::kAnalyticsFnGetAnalyticsInstanceId)); +} + +Future GetSessionId() { + FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); + auto* api = internal::FutureData::Get()->api(); + const auto future_handle = + api->SafeAlloc(internal::kAnalyticsFnGetSessionId); + int64_t session_id = 0x5E5510171D570BL; // "SESSIONIDSTUB", kinda + api->CompleteWithResult(future_handle, 0, "", session_id); + LogWarning("Analytics: GetSessionId() is not supported on Desktop."); + return Future(api, future_handle.get()); +} + +Future GetSessionIdLastResult() { + FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); + LogWarning( + "Analytics: GetSessionIdLastResult() is not supported on Desktop."); + return static_cast&>( + internal::FutureData::Get()->api()->LastResult( + internal::kAnalyticsFnGetSessionId)); +} + +} // namespace analytics +} // namespace firebase diff --git a/analytics/src/analytics_desktop_dynamic.c b/analytics/src/analytics_desktop_dynamic.c new file mode 100644 index 0000000000..e629e59474 --- /dev/null +++ b/analytics/src/analytics_desktop_dynamic.c @@ -0,0 +1,295 @@ +// 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. + +// Generated from analytics.h by generate_windows_stubs.py + +#include "analytics/src/analytics_desktop_dynamic.h" + +#include + +static void* g_stub_memory = NULL; + +// clang-format off + +// Number of Google Analytics functions expected to be loaded from the DLL. +const int FirebaseAnalytics_DynamicFunctionCount = 19; + +#if defined(_WIN32) +// Google Analytics Windows DLL SHA256 hash, to be verified on load. +const unsigned char FirebaseAnalytics_WindowsDllHash[] = { + 0xc1, 0xb9, 0xff, 0x6e, 0x91, 0x19, 0xc3, 0x0b, 0xbe, 0xb7, 0x47, 0x23, 0x26, 0xdc, 0xde, 0x41, 0x8f, 0x45, 0x68, 0x2e, 0x6b, 0x82, 0x2e, 0x25, 0xee, 0xd9, 0x22, 0xfe, 0x6e, 0x3c, 0xc6, 0x98 +}; +#endif // defined(_WIN32) + +// --- Stub Function Definitions --- +// Stub for GoogleAnalytics_Item_Create +static GoogleAnalytics_Item* Stub_GoogleAnalytics_Item_Create() { + return (GoogleAnalytics_Item*)(&g_stub_memory); +} + +// Stub for GoogleAnalytics_Item_InsertInt +static void Stub_GoogleAnalytics_Item_InsertInt(GoogleAnalytics_Item* item, + const char* key, + int64_t value) { + // No return value. +} + +// Stub for GoogleAnalytics_Item_InsertDouble +static void Stub_GoogleAnalytics_Item_InsertDouble(GoogleAnalytics_Item* item, + const char* key, + double value) { + // No return value. +} + +// Stub for GoogleAnalytics_Item_InsertString +static void Stub_GoogleAnalytics_Item_InsertString(GoogleAnalytics_Item* item, + const char* key, + const char* value) { + // No return value. +} + +// Stub for GoogleAnalytics_Item_Destroy +static void Stub_GoogleAnalytics_Item_Destroy(GoogleAnalytics_Item* item) { + // No return value. +} + +// Stub for GoogleAnalytics_ItemVector_Create +static GoogleAnalytics_ItemVector* Stub_GoogleAnalytics_ItemVector_Create() { + return (GoogleAnalytics_ItemVector*)(&g_stub_memory); +} + +// Stub for GoogleAnalytics_ItemVector_InsertItem +static void Stub_GoogleAnalytics_ItemVector_InsertItem(GoogleAnalytics_ItemVector* item_vector, GoogleAnalytics_Item* item) { + // No return value. +} + +// Stub for GoogleAnalytics_ItemVector_Destroy +static void Stub_GoogleAnalytics_ItemVector_Destroy(GoogleAnalytics_ItemVector* item_vector) { + // No return value. +} + +// Stub for GoogleAnalytics_EventParameters_Create +static GoogleAnalytics_EventParameters* Stub_GoogleAnalytics_EventParameters_Create() { + return (GoogleAnalytics_EventParameters*)(&g_stub_memory); +} + +// Stub for GoogleAnalytics_EventParameters_InsertInt +static void Stub_GoogleAnalytics_EventParameters_InsertInt(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, + int64_t value) { + // No return value. +} + +// Stub for GoogleAnalytics_EventParameters_InsertDouble +static void Stub_GoogleAnalytics_EventParameters_InsertDouble(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, + double value) { + // No return value. +} + +// Stub for GoogleAnalytics_EventParameters_InsertString +static void Stub_GoogleAnalytics_EventParameters_InsertString(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, + const char* value) { + // No return value. +} + +// Stub for GoogleAnalytics_EventParameters_InsertItemVector +static void Stub_GoogleAnalytics_EventParameters_InsertItemVector(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, + GoogleAnalytics_ItemVector* value) { + // No return value. +} + +// Stub for GoogleAnalytics_EventParameters_Destroy +static void Stub_GoogleAnalytics_EventParameters_Destroy(GoogleAnalytics_EventParameters* event_parameter_map) { + // No return value. +} + +// Stub for GoogleAnalytics_LogEvent +static void Stub_GoogleAnalytics_LogEvent(const char* name, GoogleAnalytics_EventParameters* parameters) { + // No return value. +} + +// Stub for GoogleAnalytics_SetUserProperty +static void Stub_GoogleAnalytics_SetUserProperty(const char* name, + const char* value) { + // No return value. +} + +// Stub for GoogleAnalytics_SetUserId +static void Stub_GoogleAnalytics_SetUserId(const char* user_id) { + // No return value. +} + +// Stub for GoogleAnalytics_ResetAnalyticsData +static void Stub_GoogleAnalytics_ResetAnalyticsData() { + // No return value. +} + +// Stub for GoogleAnalytics_SetAnalyticsCollectionEnabled +static void Stub_GoogleAnalytics_SetAnalyticsCollectionEnabled(bool enabled) { + // No return value. +} + + +// --- Function Pointer Initializations --- +GoogleAnalytics_Item* (*ptr_GoogleAnalytics_Item_Create)() = &Stub_GoogleAnalytics_Item_Create; +void (*ptr_GoogleAnalytics_Item_InsertInt)(GoogleAnalytics_Item* item, const char* key, int64_t value) = &Stub_GoogleAnalytics_Item_InsertInt; +void (*ptr_GoogleAnalytics_Item_InsertDouble)(GoogleAnalytics_Item* item, const char* key, double value) = &Stub_GoogleAnalytics_Item_InsertDouble; +void (*ptr_GoogleAnalytics_Item_InsertString)(GoogleAnalytics_Item* item, const char* key, const char* value) = &Stub_GoogleAnalytics_Item_InsertString; +void (*ptr_GoogleAnalytics_Item_Destroy)(GoogleAnalytics_Item* item) = &Stub_GoogleAnalytics_Item_Destroy; +GoogleAnalytics_ItemVector* (*ptr_GoogleAnalytics_ItemVector_Create)() = &Stub_GoogleAnalytics_ItemVector_Create; +void (*ptr_GoogleAnalytics_ItemVector_InsertItem)(GoogleAnalytics_ItemVector* item_vector, GoogleAnalytics_Item* item) = &Stub_GoogleAnalytics_ItemVector_InsertItem; +void (*ptr_GoogleAnalytics_ItemVector_Destroy)(GoogleAnalytics_ItemVector* item_vector) = &Stub_GoogleAnalytics_ItemVector_Destroy; +GoogleAnalytics_EventParameters* (*ptr_GoogleAnalytics_EventParameters_Create)() = &Stub_GoogleAnalytics_EventParameters_Create; +void (*ptr_GoogleAnalytics_EventParameters_InsertInt)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, int64_t value) = &Stub_GoogleAnalytics_EventParameters_InsertInt; +void (*ptr_GoogleAnalytics_EventParameters_InsertDouble)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, double value) = &Stub_GoogleAnalytics_EventParameters_InsertDouble; +void (*ptr_GoogleAnalytics_EventParameters_InsertString)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, const char* value) = &Stub_GoogleAnalytics_EventParameters_InsertString; +void (*ptr_GoogleAnalytics_EventParameters_InsertItemVector)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, GoogleAnalytics_ItemVector* value) = &Stub_GoogleAnalytics_EventParameters_InsertItemVector; +void (*ptr_GoogleAnalytics_EventParameters_Destroy)(GoogleAnalytics_EventParameters* event_parameter_map) = &Stub_GoogleAnalytics_EventParameters_Destroy; +void (*ptr_GoogleAnalytics_LogEvent)(const char* name, GoogleAnalytics_EventParameters* parameters) = &Stub_GoogleAnalytics_LogEvent; +void (*ptr_GoogleAnalytics_SetUserProperty)(const char* name, const char* value) = &Stub_GoogleAnalytics_SetUserProperty; +void (*ptr_GoogleAnalytics_SetUserId)(const char* user_id) = &Stub_GoogleAnalytics_SetUserId; +void (*ptr_GoogleAnalytics_ResetAnalyticsData)() = &Stub_GoogleAnalytics_ResetAnalyticsData; +void (*ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled)(bool enabled) = &Stub_GoogleAnalytics_SetAnalyticsCollectionEnabled; + +// --- Dynamic Loader Function for Windows --- +#if defined(_WIN32) +int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle) { + int count = 0; + + if (!dll_handle) { + return count; + } + + FARPROC proc_GoogleAnalytics_Item_Create = GetProcAddress(dll_handle, "GoogleAnalytics_Item_Create"); + if (proc_GoogleAnalytics_Item_Create) { + ptr_GoogleAnalytics_Item_Create = (GoogleAnalytics_Item* (*)())proc_GoogleAnalytics_Item_Create; + count++; + } + FARPROC proc_GoogleAnalytics_Item_InsertInt = GetProcAddress(dll_handle, "GoogleAnalytics_Item_InsertInt"); + if (proc_GoogleAnalytics_Item_InsertInt) { + ptr_GoogleAnalytics_Item_InsertInt = (void (*)(GoogleAnalytics_Item* item, const char* key, int64_t value))proc_GoogleAnalytics_Item_InsertInt; + count++; + } + FARPROC proc_GoogleAnalytics_Item_InsertDouble = GetProcAddress(dll_handle, "GoogleAnalytics_Item_InsertDouble"); + if (proc_GoogleAnalytics_Item_InsertDouble) { + ptr_GoogleAnalytics_Item_InsertDouble = (void (*)(GoogleAnalytics_Item* item, const char* key, double value))proc_GoogleAnalytics_Item_InsertDouble; + count++; + } + FARPROC proc_GoogleAnalytics_Item_InsertString = GetProcAddress(dll_handle, "GoogleAnalytics_Item_InsertString"); + if (proc_GoogleAnalytics_Item_InsertString) { + ptr_GoogleAnalytics_Item_InsertString = (void (*)(GoogleAnalytics_Item* item, const char* key, const char* value))proc_GoogleAnalytics_Item_InsertString; + count++; + } + FARPROC proc_GoogleAnalytics_Item_Destroy = GetProcAddress(dll_handle, "GoogleAnalytics_Item_Destroy"); + if (proc_GoogleAnalytics_Item_Destroy) { + ptr_GoogleAnalytics_Item_Destroy = (void (*)(GoogleAnalytics_Item* item))proc_GoogleAnalytics_Item_Destroy; + count++; + } + FARPROC proc_GoogleAnalytics_ItemVector_Create = GetProcAddress(dll_handle, "GoogleAnalytics_ItemVector_Create"); + if (proc_GoogleAnalytics_ItemVector_Create) { + ptr_GoogleAnalytics_ItemVector_Create = (GoogleAnalytics_ItemVector* (*)())proc_GoogleAnalytics_ItemVector_Create; + count++; + } + FARPROC proc_GoogleAnalytics_ItemVector_InsertItem = GetProcAddress(dll_handle, "GoogleAnalytics_ItemVector_InsertItem"); + if (proc_GoogleAnalytics_ItemVector_InsertItem) { + ptr_GoogleAnalytics_ItemVector_InsertItem = (void (*)(GoogleAnalytics_ItemVector* item_vector, GoogleAnalytics_Item* item))proc_GoogleAnalytics_ItemVector_InsertItem; + count++; + } + FARPROC proc_GoogleAnalytics_ItemVector_Destroy = GetProcAddress(dll_handle, "GoogleAnalytics_ItemVector_Destroy"); + if (proc_GoogleAnalytics_ItemVector_Destroy) { + ptr_GoogleAnalytics_ItemVector_Destroy = (void (*)(GoogleAnalytics_ItemVector* item_vector))proc_GoogleAnalytics_ItemVector_Destroy; + count++; + } + FARPROC proc_GoogleAnalytics_EventParameters_Create = GetProcAddress(dll_handle, "GoogleAnalytics_EventParameters_Create"); + if (proc_GoogleAnalytics_EventParameters_Create) { + ptr_GoogleAnalytics_EventParameters_Create = (GoogleAnalytics_EventParameters* (*)())proc_GoogleAnalytics_EventParameters_Create; + count++; + } + FARPROC proc_GoogleAnalytics_EventParameters_InsertInt = GetProcAddress(dll_handle, "GoogleAnalytics_EventParameters_InsertInt"); + if (proc_GoogleAnalytics_EventParameters_InsertInt) { + ptr_GoogleAnalytics_EventParameters_InsertInt = (void (*)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, int64_t value))proc_GoogleAnalytics_EventParameters_InsertInt; + count++; + } + FARPROC proc_GoogleAnalytics_EventParameters_InsertDouble = GetProcAddress(dll_handle, "GoogleAnalytics_EventParameters_InsertDouble"); + if (proc_GoogleAnalytics_EventParameters_InsertDouble) { + ptr_GoogleAnalytics_EventParameters_InsertDouble = (void (*)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, double value))proc_GoogleAnalytics_EventParameters_InsertDouble; + count++; + } + FARPROC proc_GoogleAnalytics_EventParameters_InsertString = GetProcAddress(dll_handle, "GoogleAnalytics_EventParameters_InsertString"); + if (proc_GoogleAnalytics_EventParameters_InsertString) { + ptr_GoogleAnalytics_EventParameters_InsertString = (void (*)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, const char* value))proc_GoogleAnalytics_EventParameters_InsertString; + count++; + } + FARPROC proc_GoogleAnalytics_EventParameters_InsertItemVector = GetProcAddress(dll_handle, "GoogleAnalytics_EventParameters_InsertItemVector"); + if (proc_GoogleAnalytics_EventParameters_InsertItemVector) { + ptr_GoogleAnalytics_EventParameters_InsertItemVector = (void (*)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, GoogleAnalytics_ItemVector* value))proc_GoogleAnalytics_EventParameters_InsertItemVector; + count++; + } + FARPROC proc_GoogleAnalytics_EventParameters_Destroy = GetProcAddress(dll_handle, "GoogleAnalytics_EventParameters_Destroy"); + if (proc_GoogleAnalytics_EventParameters_Destroy) { + ptr_GoogleAnalytics_EventParameters_Destroy = (void (*)(GoogleAnalytics_EventParameters* event_parameter_map))proc_GoogleAnalytics_EventParameters_Destroy; + count++; + } + FARPROC proc_GoogleAnalytics_LogEvent = GetProcAddress(dll_handle, "GoogleAnalytics_LogEvent"); + if (proc_GoogleAnalytics_LogEvent) { + ptr_GoogleAnalytics_LogEvent = (void (*)(const char* name, GoogleAnalytics_EventParameters* parameters))proc_GoogleAnalytics_LogEvent; + count++; + } + FARPROC proc_GoogleAnalytics_SetUserProperty = GetProcAddress(dll_handle, "GoogleAnalytics_SetUserProperty"); + if (proc_GoogleAnalytics_SetUserProperty) { + ptr_GoogleAnalytics_SetUserProperty = (void (*)(const char* name, const char* value))proc_GoogleAnalytics_SetUserProperty; + count++; + } + FARPROC proc_GoogleAnalytics_SetUserId = GetProcAddress(dll_handle, "GoogleAnalytics_SetUserId"); + if (proc_GoogleAnalytics_SetUserId) { + ptr_GoogleAnalytics_SetUserId = (void (*)(const char* user_id))proc_GoogleAnalytics_SetUserId; + count++; + } + FARPROC proc_GoogleAnalytics_ResetAnalyticsData = GetProcAddress(dll_handle, "GoogleAnalytics_ResetAnalyticsData"); + if (proc_GoogleAnalytics_ResetAnalyticsData) { + ptr_GoogleAnalytics_ResetAnalyticsData = (void (*)())proc_GoogleAnalytics_ResetAnalyticsData; + count++; + } + FARPROC proc_GoogleAnalytics_SetAnalyticsCollectionEnabled = GetProcAddress(dll_handle, "GoogleAnalytics_SetAnalyticsCollectionEnabled"); + if (proc_GoogleAnalytics_SetAnalyticsCollectionEnabled) { + ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled = (void (*)(bool enabled))proc_GoogleAnalytics_SetAnalyticsCollectionEnabled; + count++; + } + + return count; +} + +void FirebaseAnalytics_UnloadDynamicFunctions(void) { + ptr_GoogleAnalytics_Item_Create = &Stub_GoogleAnalytics_Item_Create; + ptr_GoogleAnalytics_Item_InsertInt = &Stub_GoogleAnalytics_Item_InsertInt; + ptr_GoogleAnalytics_Item_InsertDouble = &Stub_GoogleAnalytics_Item_InsertDouble; + ptr_GoogleAnalytics_Item_InsertString = &Stub_GoogleAnalytics_Item_InsertString; + ptr_GoogleAnalytics_Item_Destroy = &Stub_GoogleAnalytics_Item_Destroy; + ptr_GoogleAnalytics_ItemVector_Create = &Stub_GoogleAnalytics_ItemVector_Create; + ptr_GoogleAnalytics_ItemVector_InsertItem = &Stub_GoogleAnalytics_ItemVector_InsertItem; + ptr_GoogleAnalytics_ItemVector_Destroy = &Stub_GoogleAnalytics_ItemVector_Destroy; + ptr_GoogleAnalytics_EventParameters_Create = &Stub_GoogleAnalytics_EventParameters_Create; + ptr_GoogleAnalytics_EventParameters_InsertInt = &Stub_GoogleAnalytics_EventParameters_InsertInt; + ptr_GoogleAnalytics_EventParameters_InsertDouble = &Stub_GoogleAnalytics_EventParameters_InsertDouble; + ptr_GoogleAnalytics_EventParameters_InsertString = &Stub_GoogleAnalytics_EventParameters_InsertString; + ptr_GoogleAnalytics_EventParameters_InsertItemVector = &Stub_GoogleAnalytics_EventParameters_InsertItemVector; + ptr_GoogleAnalytics_EventParameters_Destroy = &Stub_GoogleAnalytics_EventParameters_Destroy; + ptr_GoogleAnalytics_LogEvent = &Stub_GoogleAnalytics_LogEvent; + ptr_GoogleAnalytics_SetUserProperty = &Stub_GoogleAnalytics_SetUserProperty; + ptr_GoogleAnalytics_SetUserId = &Stub_GoogleAnalytics_SetUserId; + ptr_GoogleAnalytics_ResetAnalyticsData = &Stub_GoogleAnalytics_ResetAnalyticsData; + ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled = &Stub_GoogleAnalytics_SetAnalyticsCollectionEnabled; +} + +#endif // defined(_WIN32) +// clang-format on diff --git a/analytics/src/analytics_desktop_dynamic.h b/analytics/src/analytics_desktop_dynamic.h new file mode 100644 index 0000000000..4d7560388b --- /dev/null +++ b/analytics/src/analytics_desktop_dynamic.h @@ -0,0 +1,139 @@ +// 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. + +// Generated from analytics.h by generate_windows_stubs.py + +#ifndef FIREBASE_ANALYTICS_SRC_WINDOWS_ANALYTICS_DESKTOP_DYNAMIC_H_ +#define FIREBASE_ANALYTICS_SRC_WINDOWS_ANALYTICS_DESKTOP_DYNAMIC_H_ + +#include // needed for bool type in pure C + +// --- Copied from original header --- +#include + +/** + * @brief Opaque type for an item. + * + * This type is an opaque object that represents an item in an item vector. + * + * The caller is responsible for creating the item using the + * GoogleAnalytics_Item_Create() function, and destroying it using the + * GoogleAnalytics_Item_Destroy() function, unless it has been added to an + * item vector, in which case it will be destroyed at that time. + */ +typedef struct GoogleAnalytics_Item_Opaque GoogleAnalytics_Item; + +/** + * @brief Opaque type for an item vector. + * + * This type is an opaque object that represents a list of items. It is + * used to pass item vectors to the + * GoogleAnalytics_EventParameters_InsertItemVector() function. + * + * The caller is responsible for creating the item vector using the + * GoogleAnalytics_ItemVector_Create() function, and destroying it using the + * GoogleAnalytics_ItemVector_Destroy() function, unless it has been added + * to an event parameter map, in which case it will be destroyed at that time. + */ +typedef struct GoogleAnalytics_ItemVector_Opaque GoogleAnalytics_ItemVector; + +/** + * @brief Opaque type for an event parameter map. + * + * This type is an opaque object that represents a dictionary of event + * parameters. It is used to pass event parameters to the + * GoogleAnalytics_LogEvent() function. + * + * The caller is responsible for creating the event parameter map using the + * GoogleAnalytics_EventParameters_Create() function, and destroying it using + * the GoogleAnalytics_EventParameters_Destroy() function, unless it has been + * logged, in which case it will be destroyed automatically. + */ +typedef struct GoogleAnalytics_EventParameters_Opaque + GoogleAnalytics_EventParameters; + +// --- End of copied section --- + +#ifdef __cplusplus +extern "C" { +#endif + +// --- Function Pointer Declarations --- +// clang-format off +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 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 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 void (*ptr_GoogleAnalytics_EventParameters_Destroy)(GoogleAnalytics_EventParameters* event_parameter_map); +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); + +#define GoogleAnalytics_Item_Create ptr_GoogleAnalytics_Item_Create +#define GoogleAnalytics_Item_InsertInt ptr_GoogleAnalytics_Item_InsertInt +#define GoogleAnalytics_Item_InsertDouble ptr_GoogleAnalytics_Item_InsertDouble +#define GoogleAnalytics_Item_InsertString ptr_GoogleAnalytics_Item_InsertString +#define GoogleAnalytics_Item_Destroy ptr_GoogleAnalytics_Item_Destroy +#define GoogleAnalytics_ItemVector_Create ptr_GoogleAnalytics_ItemVector_Create +#define GoogleAnalytics_ItemVector_InsertItem ptr_GoogleAnalytics_ItemVector_InsertItem +#define GoogleAnalytics_ItemVector_Destroy ptr_GoogleAnalytics_ItemVector_Destroy +#define GoogleAnalytics_EventParameters_Create ptr_GoogleAnalytics_EventParameters_Create +#define GoogleAnalytics_EventParameters_InsertInt ptr_GoogleAnalytics_EventParameters_InsertInt +#define GoogleAnalytics_EventParameters_InsertDouble ptr_GoogleAnalytics_EventParameters_InsertDouble +#define GoogleAnalytics_EventParameters_InsertString ptr_GoogleAnalytics_EventParameters_InsertString +#define GoogleAnalytics_EventParameters_InsertItemVector ptr_GoogleAnalytics_EventParameters_InsertItemVector +#define GoogleAnalytics_EventParameters_Destroy ptr_GoogleAnalytics_EventParameters_Destroy +#define GoogleAnalytics_LogEvent ptr_GoogleAnalytics_LogEvent +#define GoogleAnalytics_SetUserProperty ptr_GoogleAnalytics_SetUserProperty +#define GoogleAnalytics_SetUserId ptr_GoogleAnalytics_SetUserId +#define GoogleAnalytics_ResetAnalyticsData ptr_GoogleAnalytics_ResetAnalyticsData +#define GoogleAnalytics_SetAnalyticsCollectionEnabled ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled +// clang-format on + +// Number of Google Analytics functions expected to be loaded from the DLL. +extern const int FirebaseAnalytics_DynamicFunctionCount; + +// --- Dynamic Loader Declaration for Windows --- +#if defined(_WIN32) + +#include + +// Google Analytics Windows DLL SHA256 hash, to be verified before loading. +extern const unsigned char FirebaseAnalytics_WindowsDllHash[32]; + +// Load Analytics functions from the given DLL handle into function pointers. +// Returns the number of functions successfully loaded. +int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle); + +// Reset all function pointers back to stubs. +void FirebaseAnalytics_UnloadDynamicFunctions(void); + +#endif // defined(_WIN32) + +#ifdef __cplusplus +} +#endif + +#endif // FIREBASE_ANALYTICS_SRC_WINDOWS_ANALYTICS_DESKTOP_DYNAMIC_H_ diff --git a/analytics/src/analytics_stub.cc b/analytics/src/analytics_stub.cc deleted file mode 100644 index 37c4b3520b..0000000000 --- a/analytics/src/analytics_stub.cc +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2016 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. - */ - -#include - -#include "analytics/src/analytics_common.h" -#include "analytics/src/include/firebase/analytics.h" -#include "app/src/assert.h" -#include "app/src/include/firebase/version.h" - -namespace firebase { -namespace analytics { - -DEFINE_FIREBASE_VERSION_STRING(FirebaseAnalytics); - -static bool g_initialized = false; -static int g_fake_instance_id = 0; - -// Initialize the API. -void Initialize(const ::firebase::App& /*app*/) { - g_initialized = true; - internal::RegisterTerminateOnDefaultAppDestroy(); - internal::FutureData::Create(); - g_fake_instance_id = 0; -} - -namespace internal { - -// Determine whether the analytics module is initialized. -bool IsInitialized() { return g_initialized; } - -} // namespace internal - -// Terminate the API. -void Terminate() { - internal::FutureData::Destroy(); - internal::UnregisterTerminateOnDefaultAppDestroy(); - g_initialized = false; -} - -// Enable / disable measurement and reporting. -void SetAnalyticsCollectionEnabled(bool /*enabled*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Enable / disable measurement and reporting. -void SetConsent(const std::map& consent_settings) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Log an event with one string parameter. -void LogEvent(const char* /*name*/, const char* /*parameter_name*/, - const char* /*parameter_value*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Log an event with one double parameter. -void LogEvent(const char* /*name*/, const char* /*parameter_name*/, - const double /*parameter_value*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Log an event with one 64-bit integer parameter. -void LogEvent(const char* /*name*/, const char* /*parameter_name*/, - const int64_t /*parameter_value*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Log an event with one integer parameter (stored as a 64-bit integer). -void LogEvent(const char* name, const char* parameter_name, - const int parameter_value) { - LogEvent(name, parameter_name, static_cast(parameter_value)); -} - -// Log an event with no parameters. -void LogEvent(const char* name) { - LogEvent(name, static_cast(nullptr), - static_cast(0)); -} - -// Log an event with associated parameters. -void LogEvent(const char* /*name*/, const Parameter* /*parameters*/, - size_t /*number_of_parameters*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -/// Initiates on-device conversion measurement given a user email address on iOS -/// (no-op on Android). On iOS, requires dependency -/// GoogleAppMeasurementOnDeviceConversion to be linked in, otherwise it is a -/// no-op. -void InitiateOnDeviceConversionMeasurementWithEmailAddress( - const char* email_address) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -void InitiateOnDeviceConversionMeasurementWithHashedEmailAddress( - std::vector email_address) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -/// Initiates on-device conversion measurement given a phone number on iOS -/// (no-op on Android). On iOS, requires dependency -/// GoogleAppMeasurementOnDeviceConversion to be linked in, otherwise it is a -/// no-op. -void InitiateOnDeviceConversionMeasurementWithPhoneNumber( - const char* phone_number) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -void InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber( - std::vector phone_number_hash) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Set a user property to the given value. -void SetUserProperty(const char* /*name*/, const char* /*value*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Sets the user ID property. This feature must be used in accordance with -// -// Google's Privacy Policy -void SetUserId(const char* /*user_id*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -// Sets the duration of inactivity that terminates the current session. -void SetSessionTimeoutDuration(int64_t /*milliseconds*/) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); -} - -void ResetAnalyticsData() { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); - g_fake_instance_id++; -} - -Future GetAnalyticsInstanceId() { - FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); - auto* api = internal::FutureData::Get()->api(); - const auto future_handle = - api->SafeAlloc(internal::kAnalyticsFnGetAnalyticsInstanceId); - std::string instance_id = std::string("FakeAnalyticsInstanceId"); - { - std::stringstream ss; - ss << g_fake_instance_id; - instance_id += ss.str(); - } - api->CompleteWithResult(future_handle, 0, "", instance_id); - return Future(api, future_handle.get()); -} - -Future GetAnalyticsInstanceIdLastResult() { - FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); - return static_cast&>( - internal::FutureData::Get()->api()->LastResult( - internal::kAnalyticsFnGetAnalyticsInstanceId)); -} - -Future GetSessionId() { - FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); - auto* api = internal::FutureData::Get()->api(); - const auto future_handle = - api->SafeAlloc(internal::kAnalyticsFnGetSessionId); - int64_t session_id = 0x5E5510171D570BL; // "SESSIONIDSTUB", kinda - api->CompleteWithResult(future_handle, 0, "", session_id); - return Future(api, future_handle.get()); -} - -Future GetSessionIdLastResult() { - FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized()); - return static_cast&>( - internal::FutureData::Get()->api()->LastResult( - internal::kAnalyticsFnGetSessionId)); -} - -} // namespace analytics -} // namespace firebase diff --git a/analytics/src/analytics_windows.cc b/analytics/src/analytics_windows.cc new file mode 100644 index 0000000000..2eb7ad0efd --- /dev/null +++ b/analytics/src/analytics_windows.cc @@ -0,0 +1,302 @@ +// 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. + +#include "analytics/src/analytics_windows.h" + +#include +#include + +#include +#include +#include +#include + +#include "app/src/log.h" + +#define LOG_TAG "VerifyAndLoadAnalyticsLibrary: " + +namespace firebase { +namespace analytics { +namespace internal { + +// Helper function to retrieve the full path of the current executable. +// Returns an empty string on failure. +static std::wstring GetExecutablePath() { + std::vector buffer; + // First attempt with MAX_PATH + 1 + buffer.reserve(MAX_PATH + 1); + buffer.resize(MAX_PATH + 1); + + DWORD length = GetModuleFileNameW(NULL, buffer.data(), + static_cast(buffer.size())); + + if (length == 0) { + DWORD error_code = GetLastError(); + LogError(LOG_TAG "Failed to get executable path. Error: %u", error_code); + return std::wstring(); + } + + if (length < buffer.size()) { + // Executable path fit in our buffer, return it. + return std::wstring(buffer.data(), length); + } else { + DWORD error_code = GetLastError(); + if (error_code == ERROR_INSUFFICIENT_BUFFER) { + // Buffer was too small, try a larger one. + const size_t kLongPathMax = 65536 + 1; + + buffer.clear(); + buffer.reserve(kLongPathMax); + buffer.resize(kLongPathMax); + + DWORD length_large = GetModuleFileNameW( + NULL, buffer.data(), static_cast(buffer.size())); + + if (length_large == 0) { + DWORD error_code_large = GetLastError(); + LogError(LOG_TAG "Failed to get executable long path. Error: %u", + error_code_large); + return std::wstring(); + } + + if (length_large < buffer.size()) { + return std::wstring(buffer.data(), length_large); + } else { + // If length_large is still equal to or greater than buffer size, + // regardless of error, the path is too long for even the large buffer. + DWORD error_code_large = GetLastError(); + LogError(LOG_TAG "Executable path too long. Error: %u", + error_code_large); + return std::wstring(); + } + } else { + // length >= buffer.size() but not ERROR_INSUFFICIENT_BUFFER. + LogError(LOG_TAG "Failed to get executable path. Error: %u", error_code); + return std::wstring(); + } + } +} + +// Helper function to calculate SHA256 hash of a file. +static std::vector CalculateFileSha256(HANDLE hFile) { + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + std::vector result_hash_value; + + if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "CalculateFileSha256.SetFilePointer failed. Error: %u", + dwError); + return result_hash_value; + } + + // Acquire Crypto Provider. + // Using CRYPT_VERIFYCONTEXT for operations that don't require private key + // access. + if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, + CRYPT_VERIFYCONTEXT)) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG + "CalculateFileSha256.CryptAcquireContextW failed. Error: %u", + dwError); + return result_hash_value; + } + + if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "CalculateFileSha256.CryptCreateHash failed. Error: %u", + dwError); + CryptReleaseContext(hProv, 0); + return result_hash_value; + } + + BYTE rgbFile[1024]; + DWORD cbRead = 0; + BOOL bReadSuccessLoop = TRUE; + + while (true) { + bReadSuccessLoop = ReadFile(hFile, rgbFile, sizeof(rgbFile), &cbRead, NULL); + if (!bReadSuccessLoop) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "CalculateFileSha256.ReadFile failed. Error: %u", + dwError); + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return result_hash_value; + } + if (cbRead == 0) { + break; + } + if (!CryptHashData(hHash, rgbFile, cbRead, 0)) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "CalculateFileSha256.CryptHashData failed. Error: %u", + dwError); + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return result_hash_value; + } + } + + DWORD cbHashValue = 0; + DWORD dwCount = sizeof(DWORD); + if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHashValue, &dwCount, + 0)) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG + "CalculateFileSha256.CryptGetHashParam " + "(HP_HASHSIZE) failed. Error: " + "%u", + dwError); + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return result_hash_value; + } + + result_hash_value.resize(cbHashValue); + if (!CryptGetHashParam(hHash, HP_HASHVAL, result_hash_value.data(), + &cbHashValue, 0)) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG + "CalculateFileSha256.CryptGetHashParam (HP_HASHVAL) failed. " + "Error: %u", + dwError); + result_hash_value.clear(); + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return result_hash_value; + } + + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return result_hash_value; +} + +HMODULE VerifyAndLoadAnalyticsLibrary( + const wchar_t* library_filename, + const std::vector>& allowed_hashes) { + if (library_filename == nullptr || library_filename[0] == L'\0') { + LogError(LOG_TAG "Invalid arguments."); + return nullptr; + } + if (allowed_hashes.empty()) { + // Don't check the hash, just load the library. + LogWarning(LOG_TAG "No hash provided, using unverified Analytics DLL."); + return LoadLibraryW(library_filename); + } + + std::wstring executable_path_str = GetExecutablePath(); + + if (executable_path_str.empty()) { + // GetExecutablePath() is expected to log specific errors. + // This log indicates the failure to proceed within this function. + LogError(LOG_TAG "Can't determine executable path."); + return nullptr; + } + + size_t last_slash_pos = executable_path_str.find_last_of(L"\\"); + if (last_slash_pos == std::wstring::npos) { + // Log message updated to avoid using %ls for executable_path_str + LogError(LOG_TAG "Could not determine executable directory."); + return nullptr; + } + + std::wstring full_dll_path_str = + executable_path_str.substr(0, last_slash_pos + 1); + full_dll_path_str += library_filename; // library_filename is the filename + + HANDLE hFile = + CreateFileW(full_dll_path_str.c_str(), GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + DWORD dwError = GetLastError(); + // If the DLL is simply not found, silently proceed to stub mode without + // logging an error. For other errors (e.g., access denied on an existing + // file), log them as it's an unexpected issue. + if (dwError != ERROR_FILE_NOT_FOUND && dwError != ERROR_PATH_NOT_FOUND) { + LogError(LOG_TAG "Failed to open Analytics DLL. Error: %u", dwError); + } + return nullptr; // In all CreateFileW failure cases, return nullptr to + // fall back to stub mode. + } + + OVERLAPPED overlapped = {0}; + // Attempt to lock the entire file exclusively (LOCKFILE_EXCLUSIVE_LOCK). + // This helps ensure no other process modifies the file while we are + // verifying and loading it. + BOOL bFileLocked = LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 0xFFFFFFFF, + 0xFFFFFFFF, &overlapped); + if (!bFileLocked) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "Failed to lock Analytics DLL. Error: %u", dwError); + CloseHandle(hFile); + return nullptr; + } + + HMODULE hModule = nullptr; + + std::vector calculated_hash = CalculateFileSha256(hFile); + + if (calculated_hash.empty()) { + LogError(LOG_TAG "Hash failed for Analytics DLL."); + } else { + bool hash_matched = false; + for (const auto& expected_hash : allowed_hashes) { + if (calculated_hash.size() != expected_hash.size()) { + LogDebug(LOG_TAG + "Hash size mismatch for Analytics DLL. Expected: %zu, " + "Calculated: %zu. Trying next allowed hash.", + expected_hash.size(), calculated_hash.size()); + continue; + } + if (memcmp(calculated_hash.data(), expected_hash.data(), + expected_hash.size()) == 0) { + hash_matched = true; + break; + } + } + + if (hash_matched) { + LogDebug(LOG_TAG "Successfully verified Analytics DLL."); + // Load the library. When loading with a full path string, other + // directories are not searched. + hModule = LoadLibraryW(full_dll_path_str.c_str()); + if (hModule == NULL) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "Library load failed for Analytics DLL. Error: %u", + dwError); + } + } else { + LogError(LOG_TAG "Hash mismatch for Analytics DLL."); + } + } + + if (bFileLocked) { + if (!UnlockFileEx(hFile, 0, 0xFFFFFFFF, 0xFFFFFFFF, &overlapped)) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "Failed to unlock Analytics DLL. Error: %u", dwError); + } + } + + if (hFile != INVALID_HANDLE_VALUE) { + if (!CloseHandle(hFile)) { + DWORD dwError = GetLastError(); + LogError(LOG_TAG "Failed to close Analytics DLL. Error: %u", dwError); + } + } + return hModule; +} + +} // namespace internal +} // namespace analytics +} // namespace firebase diff --git a/analytics/src/analytics_windows.h b/analytics/src/analytics_windows.h new file mode 100644 index 0000000000..f18683e026 --- /dev/null +++ b/analytics/src/analytics_windows.h @@ -0,0 +1,34 @@ +// 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 FIREBASE_ANALYTICS_SRC_ANALYTICS_WINDOWS_H_ +#define FIREBASE_ANALYTICS_SRC_ANALYTICS_WINDOWS_H_ + +#include + +#include + +namespace firebase { +namespace analytics { +namespace internal { + +HMODULE VerifyAndLoadAnalyticsLibrary( + const wchar_t* library_filename, + const std::vector>& allowed_hashes); + +} // namespace internal +} // namespace analytics +} // namespace firebase + +#endif // FIREBASE_ANALYTICS_SRC_ANALYTICS_WINDOWS_H_