From 3a2358b169706a08655061561e48c9b297a3baa0 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 11:05:07 -0700 Subject: [PATCH 01/56] Added debug device ID fetch to Android. --- .../android_firebase_test_framework.cc | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/testing/test_framework/src/android/android_firebase_test_framework.cc b/testing/test_framework/src/android/android_firebase_test_framework.cc index d1289d0750..33f6ee7915 100644 --- a/testing/test_framework/src/android/android_firebase_test_framework.cc +++ b/testing/test_framework/src/android/android_firebase_test_framework.cc @@ -267,4 +267,50 @@ int FirebaseTest::GetGooglePlayServicesVersion() { return static_cast(result); } +std::string FirebaseTest::GetDebugDeviceId() { + static char* device_id = nullptr; + if (!device_id) { + device_id = ""; + + JNIEnv* env = app_framework::GetJniEnv(); + jobject activity = app_framework::GetActivity(); + jclass test_helper_class = app_framework::FindClass( + env, activity, "com/google/firebase/example/TestHelper"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return ""; + } + jmethodID get_id = + env->GetMethodID(test_helper_class, "getDebugDeviceId", + "(Landroid/content/Context;)Ljava/lang/String;"); + + jobject device_id_obj = + env->CallStaticObjectMethod(test_helper_class, get_id, activity); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return ""; + } + jstring device_id_jstring = static_cast(device_id_obj); + const char* device_id_text = + env->GetStringUTFChars(device_id_jstring, nullptr); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return ""; + } + device_id = new char[strlen(device_id_text) + 1]; + strcpy(device_id, device_id_text); + + env->ReleaseStringUTFChars(device_id_jstring, device_id_text); + env->DeleteLocalRef(device_id_jstring); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + return device_id; +} + } // namespace firebase_test_framework From 81a88ee937ad4a7db25ab17c0fccdcc51b178214 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 11:18:44 -0700 Subject: [PATCH 02/56] Add Java method for obtaining Android ID from secure settings. --- .../google/firebase/example/TestHelper.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java index f2fb23d5ea..3d1b328330 100644 --- a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java +++ b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java @@ -14,12 +14,19 @@ package com.google.firebase.example; +import android.content.ContentResolver; import android.content.Context; import android.os.Build; +import android.provider.Settings; import android.util.Log; + import java.lang.Class; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collection; /** * A simple class with test helper methods. @@ -58,4 +65,34 @@ public static int getGooglePlayServicesVersion(Context context) { } return 0; } + + private static String md5(String string) { + // Old devices have a bug where OpenSSL can leave MessageDigest in a bad state, but trying + // multiple times seems to clear it. + int maxAttempts = 3; + for (int i = 0; i < maxAttempts; ++i) { + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(string.getBytes()); + return String.format("%032X", new BigInteger(1, md5.digest())); + } catch (NoSuchAlgorithmException e) { + // Try again. + } catch (ArithmeticException ex) { + return ""; + } + } + return ""; + } + + public static String getDebugDeviceId(Context context) { + if (isRunningOnEmulator()) { + return "emulator"; + } + ContentResolver contentResolver = context.getContentResolver(); + String androidId = + contentResolver == null + ? "null" + : Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID); + return androidId; + } } From 661af548f956a06bbcd44d77c81337d48ad9a86a Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 11:23:58 -0700 Subject: [PATCH 03/56] Update android ID algorithm. --- .../google/firebase/example/TestHelper.java | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java index 3d1b328330..a9975405cc 100644 --- a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java +++ b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java @@ -66,33 +66,30 @@ public static int getGooglePlayServicesVersion(Context context) { return 0; } - private static String md5(String string) { - // Old devices have a bug where OpenSSL can leave MessageDigest in a bad state, but trying - // multiple times seems to clear it. - int maxAttempts = 3; - for (int i = 0; i < maxAttempts; ++i) { - try { + private static String md5(String message) { + String result = null; + if (message == null || message.length() == 0) + return ""; + try { MessageDigest md5 = MessageDigest.getInstance("MD5"); - md5.update(string.getBytes()); - return String.format("%032X", new BigInteger(1, md5.digest())); - } catch (NoSuchAlgorithmException e) { - // Try again. - } catch (ArithmeticException ex) { + md5.update(message.getBytes(), 0, message.length()); + result = String.format("%032X", new BigInteger(1, md5.digest())); + } catch (NoSuchAlgorithmException ex) { + result = ""; + } catch (ArithmeticException ex) { return ""; - } } - return ""; + return result; } public static String getDebugDeviceId(Context context) { - if (isRunningOnEmulator()) { - return "emulator"; - } - ContentResolver contentResolver = context.getContentResolver(); - String androidId = - contentResolver == null - ? "null" - : Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID); - return androidId; + if (isRunningOnEmulator()) { + return "emulator"; + } + ContentResolver contentResolver = context.getContentResolver(); + if (contentResolver == null) { + return "error"; + } + return Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID); } } From baddc6de0c745c59bd0231b19dd3b0173888de3f Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 12:40:16 -0700 Subject: [PATCH 04/56] Add flatbuffers patch when building for Android. --- messaging/messaging_java/build.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/messaging/messaging_java/build.gradle b/messaging/messaging_java/build.gradle index 5289dac1a8..9c5826bb6b 100644 --- a/messaging/messaging_java/build.gradle +++ b/messaging/messaging_java/build.gradle @@ -81,6 +81,14 @@ afterEvaluate { 'https://github.com/google/flatbuffers.git', flatbuffersDir } + exec { + executable 'git' + args 'apply', + '../../scripts/git/patches/flatbuffers/0001-remove-unused-var.patch', + '--verbose', + '--directory', + 'messaging/messaging_java/build/flatbuffers' + } } // Locate or build flatc. From 4a4a32eb0212b828351b338a023be1d88c7aa658 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 13:01:59 -0700 Subject: [PATCH 05/56] Add stub Android files. --- gma/CMakeLists.txt | 2 +- .../ump/consent_info_internal_android.cc | 99 +++++++++++++++++++ .../ump/consent_info_internal_android.h | 57 +++++++++++ 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 gma/src/android/ump/consent_info_internal_android.cc create mode 100644 gma/src/android/ump/consent_info_internal_android.h diff --git a/gma/CMakeLists.txt b/gma/CMakeLists.txt index 440d0db7f0..75716ac305 100644 --- a/gma/CMakeLists.txt +++ b/gma/CMakeLists.txt @@ -40,7 +40,7 @@ binary_to_array("gma_resources" # Source files used by the Android implementation. set(android_SRCS ${gma_resources_source} - src/stub/ump/consent_info_internal_stub.cc + src/stub/ump/consent_info_internal_android.cc src/android/ad_request_converter.cc src/android/ad_error_android.cc src/android/adapter_response_info_android.cc diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc new file mode 100644 index 0000000000..196784c42f --- /dev/null +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -0,0 +1,99 @@ +/* + * Copyright 2023 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 "gma/src/android/ump/consent_info_internal_android.h" + +#include "app/src/thread.h" + +namespace firebase { +namespace gma { +namespace ump { +namespace internal { + +// This explicitly implements the constructor for the outer class, +// ConsentInfoInternal. +ConsentInfoInternal* ConsentInfoInternal::CreateInstance() { + return new ConsentInfoInternalAndroid(); +} + +ConsentInfoInternalAndroid::ConsentInfoInternalAndroid() {} + +ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() {} + +Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( + const ConsentRequestParameters& params) { + SafeFutureHandle handle = + CreateFuture(kConsentInfoFnRequestConsentInfoUpdate); + + CompleteFuture(handle, kConsentRequestSuccess); + return MakeFuture(futures(), handle); +} + +ConsentStatus ConsentInfoInternalAndroid::GetConsentStatus() { + return kConsentStatusUnknown; +} +ConsentFormStatus ConsentInfoInternalAndroid::GetConsentFormStatus() { + return kConsentFormStatusUnknown; +} + +Future ConsentInfoInternalAndroid::LoadConsentForm() { + SafeFutureHandle handle = CreateFuture(kConsentInfoFnLoadConsentForm); + + CompleteFuture(handle, kConsentFormSuccess); + return MakeFuture(futures(), handle); +} + +Future ConsentInfoInternalAndroid::ShowConsentForm(FormParent parent) { + SafeFutureHandle handle = CreateFuture(kConsentInfoFnShowConsentForm); + + CompleteFuture(handle, kConsentRequestSuccess); + return MakeFuture(futures(), handle); +} + +Future ConsentInfoInternalAndroid::LoadAndShowConsentFormIfRequired( + FormParent parent) { + SafeFutureHandle handle = + CreateFuture(kConsentInfoFnLoadAndShowConsentFormIfRequired); + + CompleteFuture(handle, kConsentRequestSuccess); + return MakeFuture(futures(), handle); +} + +PrivacyOptionsRequirementStatus +ConsentInfoInternalAndroid::GetPrivacyOptionsRequirementStatus() { + return kPrivacyOptionsRequirementStatusUnknown; +} + +Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( + FormParent parent) { + SafeFutureHandle handle = + CreateFuture(kConsentInfoFnShowPrivacyOptionsForm); + + CompleteFuture(handle, kConsentRequestSuccess); + return MakeFuture(futures(), handle); +} + +bool ConsentInfoInternalAndroid::CanRequestAds() { + return false; +} + +void ConsentInfoInternalAndroid::Reset() { +} + +} // namespace internal +} // namespace ump +} // namespace gma +} // namespace firebase diff --git a/gma/src/android/ump/consent_info_internal_android.h b/gma/src/android/ump/consent_info_internal_android.h new file mode 100644 index 0000000000..f0b0deb06b --- /dev/null +++ b/gma/src/android/ump/consent_info_internal_android.h @@ -0,0 +1,57 @@ +/* + * Copyright 2023 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_GMA_SRC_ANDROID_UMP_CONSENT_INFO_INTERNAL_ANDROID_H_ +#define FIREBASE_GMA_SRC_ANDROID_UMP_CONSENT_INFO_INTERNAL_ANDROID_H_ + +#include "gma/src/common/ump/consent_info_internal.h" + +namespace firebase { +namespace gma { +namespace ump { +namespace internal { + +class ConsentInfoInternalAndroid : public ConsentInfoInternal { + public: + ConsentInfoInternalAndroid(); + ~ConsentInfoInternalAndroid() override; + + ConsentStatus GetConsentStatus() const override; + ConsentFormStatus GetConsentFormStatus() const override; + + Future RequestConsentInfoUpdate( + const ConsentRequestParameters& params) override; + Future LoadConsentForm() override; + Future ShowConsentForm(FormParent parent) override; + + Future LoadAndShowConsentFormIfRequired(FormParent parent) override; + + PrivacyOptionsRequirementStatus GetPrivacyOptionsRequirementStatus() override; + Future ShowPrivacyOptionsForm(FormParent parent) override; + + bool CanRequestAds() override; + + void Reset() override; + + private: +}; + +} // namespace internal +} // namespace ump +} // namespace gma +} // namespace firebase + +#endif // FIREBASE_GMA_SRC_ANDROID_UMP_CONSENT_INFO_INTERNAL_ANDROID_H_ From 885c68aa4399addae09643781fcf505d0b89c976 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 13:04:48 -0700 Subject: [PATCH 06/56] Fix path. --- gma/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gma/CMakeLists.txt b/gma/CMakeLists.txt index 75716ac305..9d63dd55de 100644 --- a/gma/CMakeLists.txt +++ b/gma/CMakeLists.txt @@ -40,7 +40,7 @@ binary_to_array("gma_resources" # Source files used by the Android implementation. set(android_SRCS ${gma_resources_source} - src/stub/ump/consent_info_internal_android.cc + src/android/ump/consent_info_internal_android.cc src/android/ad_request_converter.cc src/android/ad_error_android.cc src/android/adapter_response_info_android.cc From 089ca5ddcc5c6817b25525c52fa161d7ebe8bc76 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 19:44:20 -0700 Subject: [PATCH 07/56] Add ConsentInfoHelper class with some starting functionality. --- .../gma/internal/cpp/ConsentInfoHelper.java | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java diff --git a/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java new file mode 100644 index 0000000000..d6dceb3787 --- /dev/null +++ b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java @@ -0,0 +1,137 @@ +/* + * Copyright 2021 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. + */ + +package com.google.firebase.gma.internal.cpp; + +import android.app.Activity; +import android.content.Context; +import android.util.Log; +import com.google.android.ump.ConsentInformation; +import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateFailureListener; +import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateSuccessListener; +import com.google.android.ump.ConsentRequestParameters; +import com.google.android.ump.ConsentDebugSettings; +import com.google.android.ump.FormError; +import com.google.android.ump.UserMessagingPlatform; +import java.util.ArrayList; + +/** + * Helper class to make interactions between the GMA UMP C++ wrapper and the Android UMP API. + */ +public class ConsentInfoHelper { + // C++ nullptr for use with the callbacks. + private static final long CPP_NULLPTR = 0; + + // Synchronization object for thread safe access to: + private final Object mLock; + + private long mInternalPtr; + + private Activity mActivity; + + public ConsentInfoHelper(long consentInfoInternalPtr, Activity activity) { + mInternalPtr = consentInfoInternalPtr; + mLock = new Object(); + mActivity = activity; + // Test the callbacks and fail quickly if something's wrong. + completeRequestConsentInfoUpdate(CPP_NULLPTR, 0, ""); + } + + int getConsentStatus() { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + return consentInfo.getConsentStatus(); + } + + void requestConsentInfoUpdate(boolean tagForUnderAgeOfConsent, + int debugGeography, ArrayList debugIdList) { + ConsentDebugSettings.Builder debugSettingsBuilder = null; + + if (debugGeography != ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_DISABLED) { + debugSettingsBuilder = new ConsentDebugSettings + .Builder(mActivity) + .setDebugGeography(debugGeography); + } + if (debugIdList != null && debugIdList.size() > 0) { + if (debugSettingsBuilder == null) { + debugSettingsBuilder = new ConsentDebugSettings.Builder(mActivity); + } + for (int i=0; i < debugIdList.size(); i++) { + debugSettingsBuilder = debugSettingsBuilder.addTestDeviceHashedId(debugIdList.get(i)); + } + } + ConsentRequestParameters.Builder paramsBuilder = new ConsentRequestParameters + .Builder() + .setTagForUnderAgeOfConsent(tagForUnderAgeOfConsent); + + if (debugSettingsBuilder != null) { + paramsBuilder = paramsBuilder.setConsentDebugSettings(debugSettingsBuilder.build()); + } + + final ConsentRequestParameters params = paramsBuilder.build(); + + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + consentInfo.requestConsentInfoUpdate + (mActivity, + params, + new OnConsentInfoUpdateSuccessListener() { + @Override + public void onConsentInfoUpdateSuccess() { + synchronized (mLock) { + completeRequestConsentInfoUpdate(mInternalPtr, 0, ""); + } + } + }, + new OnConsentInfoUpdateFailureListener() { + @Override + public void onConsentInfoUpdateFailure(FormError requestConsentError) { + synchronized (mLock) { + completeRequestConsentInfoUpdate(mInternalPtr, + requestConsentError.getErrorCode(), + requestConsentError.getMessage()); + } + } + }); + } + }); + } + + + /** Disconnect the helper from the native object. */ + public void disconnect() { + synchronized (mLock) { + mInternalPtr = CPP_NULLPTR; + } + } + + public static native void completeRequestConsentInfoUpdate( + long nativeInternalPtr, int errorCode, String errorMessage); + + public static native void completeLoadConsentForm( + long nativeInternalPtr, int errorCode, String errorMessage); + + public static native void completeShowConsentForm( + long nativeInternalPtr, int errorCode, String errorMessage); + + public static native void completeLoadAndShowIfRequired( + long nativeInternalPtr, int errorCode, String errorMessage); + + public static native void completeShowPrivacyOptionsForm( + long nativeInternalPtr, int errorCode, String errorMessage); +} From 376b4b834bf3b219301beba0f9f136beb56606a8 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 19:44:42 -0700 Subject: [PATCH 08/56] Fix GetDebugDeviceId on Android. --- .../src/android/android_firebase_test_framework.cc | 2 -- testing/test_framework/src/firebase_test_framework.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/test_framework/src/android/android_firebase_test_framework.cc b/testing/test_framework/src/android/android_firebase_test_framework.cc index 33f6ee7915..a935e5531e 100644 --- a/testing/test_framework/src/android/android_firebase_test_framework.cc +++ b/testing/test_framework/src/android/android_firebase_test_framework.cc @@ -270,8 +270,6 @@ int FirebaseTest::GetGooglePlayServicesVersion() { std::string FirebaseTest::GetDebugDeviceId() { static char* device_id = nullptr; if (!device_id) { - device_id = ""; - JNIEnv* env = app_framework::GetJniEnv(); jobject activity = app_framework::GetActivity(); jclass test_helper_class = app_framework::FindClass( diff --git a/testing/test_framework/src/firebase_test_framework.h b/testing/test_framework/src/firebase_test_framework.h index cff9ef5aec..80327f0a74 100644 --- a/testing/test_framework/src/firebase_test_framework.h +++ b/testing/test_framework/src/firebase_test_framework.h @@ -538,6 +538,8 @@ class FirebaseTest : public testing::Test { // false if it failed. static bool Base64Decode(const std::string& input, std::string* output); + static std::string GetDebugDeviceId(); + firebase::App* app_; static int argc_; static char** argv_; From 98ca5ec84b69de661f553d3f30cd2b5caf8b5967 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 19:47:10 -0700 Subject: [PATCH 09/56] Format code. --- .../gma/internal/cpp/ConsentInfoHelper.java | 83 +++++++++---------- .../google/firebase/example/TestHelper.java | 15 ++-- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java index d6dceb3787..4b606927e4 100644 --- a/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java @@ -19,11 +19,11 @@ import android.app.Activity; import android.content.Context; import android.util.Log; +import com.google.android.ump.ConsentDebugSettings; import com.google.android.ump.ConsentInformation; import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateFailureListener; import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateSuccessListener; import com.google.android.ump.ConsentRequestParameters; -import com.google.android.ump.ConsentDebugSettings; import com.google.android.ump.FormError; import com.google.android.ump.UserMessagingPlatform; import java.util.ArrayList; @@ -55,29 +55,27 @@ int getConsentStatus() { return consentInfo.getConsentStatus(); } - void requestConsentInfoUpdate(boolean tagForUnderAgeOfConsent, - int debugGeography, ArrayList debugIdList) { + void requestConsentInfoUpdate( + boolean tagForUnderAgeOfConsent, int debugGeography, ArrayList debugIdList) { ConsentDebugSettings.Builder debugSettingsBuilder = null; - + if (debugGeography != ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_DISABLED) { - debugSettingsBuilder = new ConsentDebugSettings - .Builder(mActivity) - .setDebugGeography(debugGeography); + debugSettingsBuilder = + new ConsentDebugSettings.Builder(mActivity).setDebugGeography(debugGeography); } if (debugIdList != null && debugIdList.size() > 0) { - if (debugSettingsBuilder == null) { - debugSettingsBuilder = new ConsentDebugSettings.Builder(mActivity); - } - for (int i=0; i < debugIdList.size(); i++) { - debugSettingsBuilder = debugSettingsBuilder.addTestDeviceHashedId(debugIdList.get(i)); - } + if (debugSettingsBuilder == null) { + debugSettingsBuilder = new ConsentDebugSettings.Builder(mActivity); + } + for (int i = 0; i < debugIdList.size(); i++) { + debugSettingsBuilder = debugSettingsBuilder.addTestDeviceHashedId(debugIdList.get(i)); + } } - ConsentRequestParameters.Builder paramsBuilder = new ConsentRequestParameters - .Builder() - .setTagForUnderAgeOfConsent(tagForUnderAgeOfConsent); + ConsentRequestParameters.Builder paramsBuilder = + new ConsentRequestParameters.Builder().setTagForUnderAgeOfConsent(tagForUnderAgeOfConsent); if (debugSettingsBuilder != null) { - paramsBuilder = paramsBuilder.setConsentDebugSettings(debugSettingsBuilder.build()); + paramsBuilder = paramsBuilder.setConsentDebugSettings(debugSettingsBuilder.build()); } final ConsentRequestParameters params = paramsBuilder.build(); @@ -85,33 +83,28 @@ void requestConsentInfoUpdate(boolean tagForUnderAgeOfConsent, mActivity.runOnUiThread(new Runnable() { @Override public void run() { - - ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); - consentInfo.requestConsentInfoUpdate - (mActivity, - params, - new OnConsentInfoUpdateSuccessListener() { - @Override - public void onConsentInfoUpdateSuccess() { - synchronized (mLock) { - completeRequestConsentInfoUpdate(mInternalPtr, 0, ""); - } - } - }, - new OnConsentInfoUpdateFailureListener() { - @Override - public void onConsentInfoUpdateFailure(FormError requestConsentError) { - synchronized (mLock) { - completeRequestConsentInfoUpdate(mInternalPtr, - requestConsentError.getErrorCode(), - requestConsentError.getMessage()); - } - } - }); + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + consentInfo.requestConsentInfoUpdate(mActivity, params, + new OnConsentInfoUpdateSuccessListener() { + @Override + public void onConsentInfoUpdateSuccess() { + synchronized (mLock) { + completeRequestConsentInfoUpdate(mInternalPtr, 0, ""); + } + } + }, + new OnConsentInfoUpdateFailureListener() { + @Override + public void onConsentInfoUpdateFailure(FormError requestConsentError) { + synchronized (mLock) { + completeRequestConsentInfoUpdate(mInternalPtr, requestConsentError.getErrorCode(), + requestConsentError.getMessage()); + } + } + }); } - }); + }); } - /** Disconnect the helper from the native object. */ public void disconnect() { @@ -125,13 +118,13 @@ public static native void completeRequestConsentInfoUpdate( public static native void completeLoadConsentForm( long nativeInternalPtr, int errorCode, String errorMessage); - + public static native void completeShowConsentForm( long nativeInternalPtr, int errorCode, String errorMessage); - + public static native void completeLoadAndShowIfRequired( long nativeInternalPtr, int errorCode, String errorMessage); - + public static native void completeShowPrivacyOptionsForm( long nativeInternalPtr, int errorCode, String errorMessage); } diff --git a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java index a9975405cc..7f76d04baa 100644 --- a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java +++ b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java @@ -19,7 +19,6 @@ import android.os.Build; import android.provider.Settings; import android.util.Log; - import java.lang.Class; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -69,15 +68,15 @@ public static int getGooglePlayServicesVersion(Context context) { private static String md5(String message) { String result = null; if (message == null || message.length() == 0) - return ""; + return ""; try { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - md5.update(message.getBytes(), 0, message.length()); - result = String.format("%032X", new BigInteger(1, md5.digest())); + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(message.getBytes(), 0, message.length()); + result = String.format("%032X", new BigInteger(1, md5.digest())); } catch (NoSuchAlgorithmException ex) { - result = ""; + result = ""; } catch (ArithmeticException ex) { - return ""; + return ""; } return result; } @@ -88,7 +87,7 @@ public static String getDebugDeviceId(Context context) { } ContentResolver contentResolver = context.getContentResolver(); if (contentResolver == null) { - return "error"; + return "error"; } return Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID); } From f1a9a78bb70b0bf0b22611802197ffeadbd99f95 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 19:47:33 -0700 Subject: [PATCH 10/56] Add UMP dependency. --- Android/firebase_dependencies.gradle | 3 ++- gma/gma_resources/build.gradle | 1 + release_build_files/Android/firebase_dependencies.gradle | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Android/firebase_dependencies.gradle b/Android/firebase_dependencies.gradle index 2481bd4c38..08c835bf84 100644 --- a/Android/firebase_dependencies.gradle +++ b/Android/firebase_dependencies.gradle @@ -27,7 +27,8 @@ def firebaseDependenciesMap = [ 'dynamic_links' : ['com.google.firebase:firebase-dynamic-links'], 'firestore' : ['com.google.firebase:firebase-firestore'], 'functions' : ['com.google.firebase:firebase-functions'], - 'gma' : ['com.google.android.gms:play-services-ads:22.2.0'], + 'gma' : ['com.google.android.gms:play-services-ads:22.2.0', + 'com.google.android.ump:user-messaging-platform:2.1.0'], 'installations' : ['com.google.firebase:firebase-installations'], 'invites' : ['com.google.firebase:firebase-invites'], // Messaging has an additional local dependency to include. diff --git a/gma/gma_resources/build.gradle b/gma/gma_resources/build.gradle index 79dc1bb0a8..4cd4c54af4 100644 --- a/gma/gma_resources/build.gradle +++ b/gma/gma_resources/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation platform('com.google.firebase:firebase-bom:32.2.2') implementation 'com.google.firebase:firebase-analytics' implementation 'com.google.android.gms:play-services-ads:22.2.0' + implementation 'com.google.android.ump:user-messaging-platform:2.1.0' } afterEvaluate { diff --git a/release_build_files/Android/firebase_dependencies.gradle b/release_build_files/Android/firebase_dependencies.gradle index 67d8d1120f..9b26ebbb33 100644 --- a/release_build_files/Android/firebase_dependencies.gradle +++ b/release_build_files/Android/firebase_dependencies.gradle @@ -27,7 +27,8 @@ def firebaseDependenciesMap = [ 'dynamic_links' : ['com.google.firebase:firebase-dynamic-links'], 'firestore' : ['com.google.firebase:firebase-firestore'], 'functions' : ['com.google.firebase:firebase-functions'], - 'gma' : ['com.google.android.gms:play-services-ads:22.2.0'], + 'gma' : ['com.google.android.gms:play-services-ads:22.2.0', + 'com.google.android.ump:user-messaging-platform:2.1.0'], 'installations' : ['com.google.firebase:firebase-installations'], 'invites' : ['com.google.firebase:firebase-invites'], // Messaging has an additional local dependency to include. From 6f03e3cf99481349b2c235a3131bb0cd8d5900a3 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 19:47:52 -0700 Subject: [PATCH 11/56] Fix overrides. --- gma/src/android/ump/consent_info_internal_android.cc | 9 +++------ gma/src/android/ump/consent_info_internal_android.h | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 196784c42f..4e946de5d9 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -76,7 +76,7 @@ PrivacyOptionsRequirementStatus ConsentInfoInternalAndroid::GetPrivacyOptionsRequirementStatus() { return kPrivacyOptionsRequirementStatusUnknown; } - + Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( FormParent parent) { SafeFutureHandle handle = @@ -86,12 +86,9 @@ Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( return MakeFuture(futures(), handle); } -bool ConsentInfoInternalAndroid::CanRequestAds() { - return false; -} +bool ConsentInfoInternalAndroid::CanRequestAds() { return false; } -void ConsentInfoInternalAndroid::Reset() { -} +void ConsentInfoInternalAndroid::Reset() {} } // namespace internal } // namespace ump diff --git a/gma/src/android/ump/consent_info_internal_android.h b/gma/src/android/ump/consent_info_internal_android.h index f0b0deb06b..e66d1769a1 100644 --- a/gma/src/android/ump/consent_info_internal_android.h +++ b/gma/src/android/ump/consent_info_internal_android.h @@ -29,8 +29,8 @@ class ConsentInfoInternalAndroid : public ConsentInfoInternal { ConsentInfoInternalAndroid(); ~ConsentInfoInternalAndroid() override; - ConsentStatus GetConsentStatus() const override; - ConsentFormStatus GetConsentFormStatus() const override; + ConsentStatus GetConsentStatus() override; + ConsentFormStatus GetConsentFormStatus() override; Future RequestConsentInfoUpdate( const ConsentRequestParameters& params) override; From adcca1e999c12809838ccc8c845d29868f33a69f Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 20:28:49 -0700 Subject: [PATCH 12/56] Add initialization. --- .../ump/consent_info_internal_android.cc | 63 +++++++++++++++++-- .../ump/consent_info_internal_android.h | 4 ++ gma/src/common/ump/consent_info.cc | 11 +++- gma/src/common/ump/consent_info_internal.h | 9 +++ .../include/firebase/gma/ump/consent_info.h | 3 +- 5 files changed, 82 insertions(+), 8 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 4e946de5d9..a62a28f494 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -17,21 +17,76 @@ #include "gma/src/android/ump/consent_info_internal_android.h" #include "app/src/thread.h" +#include "app/src/util_android.h" namespace firebase { namespace gma { namespace ump { namespace internal { +// clang-format off +#define CONSENTINFOHELPER_METHODS(X) \ + X(Constructor, "", "(JLandroid/app/Activity;)V"), \ + X(GetConsentStatus, "getConsentStatus", "()I"), \ + X(GetConsentStatus, "requestConsentStatusUpdate", \ + "(ZILjava/util/ArrayList;)V"), \ + X(Disconnect, "disconnect", "()V") +// clang-format on + +METHOD_LOOKUP_DECLARATION(consent_info_helper, CONSENTINFOHELPER_METHODS); +METHOD_LOOKUP_DEFINITION( + consent_info_helper, + "com/google/firebase/gma/internal/cpp/ConsentInfoHelper", + CONSENTINFOHELPER_METHODS); + // This explicitly implements the constructor for the outer class, // ConsentInfoInternal. -ConsentInfoInternal* ConsentInfoInternal::CreateInstance() { - return new ConsentInfoInternalAndroid(); +ConsentInfoInternal* ConsentInfoInternal::CreateInstance(JNIEnv* jni_env, + jobject activity) { + ConsentInfoInternalAndroid ptr = + new ConsentInfoInternalAndroid(jni_env, activity); + if (!ptr.valid()) { + delete ptr; + return nullptr; + } + return ptr; } -ConsentInfoInternalAndroid::ConsentInfoInternalAndroid() {} +ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* jni_env, + jobject activity) + : helper_(nullptr) { + util::Initialize(jni_env, activity); + + const std::vector embedded_files = + util::CacheEmbeddedFiles(env, activity, + firebase::internal::EmbeddedFile::ToVector( + firebase_gma::gma_resources_filename, + firebase_gma::gma_resources_data, + firebase_gma::gma_resources_size)); + if (!consent_info_helper::CacheClassFromFiles(env, activity, + &embedded_files) != nullptr && + consent_info_helper::CacheMethodIds(env, activity)) { + util::Terminate(); + return; + } + jobject helper_ref = env->NewObject( + consent_info_helper::GetClass(), + consent_info_helper::GetMethodId(rewarded_ad_helper::kConstructor), + reinterpret_cast(this)); + util::CheckAndClearJniExceptions(env); + + FIREBASE_ASSERT(helper_ref); + helper_ = env->NewGlobalRef(helper_ref); + FIREBASE_ASSERT(helper_); + env->DeleteLocalRef(helper_ref); +} -ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() {} +ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { + env->DeleteGlobalRef(helper_); + helper_ = nullptr; + consent_info_helper::Terminate(); + util::Terminate(); +} Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( const ConsentRequestParameters& params) { diff --git a/gma/src/android/ump/consent_info_internal_android.h b/gma/src/android/ump/consent_info_internal_android.h index e66d1769a1..e5e7c6e86b 100644 --- a/gma/src/android/ump/consent_info_internal_android.h +++ b/gma/src/android/ump/consent_info_internal_android.h @@ -17,6 +17,7 @@ #ifndef FIREBASE_GMA_SRC_ANDROID_UMP_CONSENT_INFO_INTERNAL_ANDROID_H_ #define FIREBASE_GMA_SRC_ANDROID_UMP_CONSENT_INFO_INTERNAL_ANDROID_H_ +#include "app/src/util_android.h" #include "gma/src/common/ump/consent_info_internal.h" namespace firebase { @@ -46,7 +47,10 @@ class ConsentInfoInternalAndroid : public ConsentInfoInternal { void Reset() override; + bool valid() { return (helper_ != nullptr); } + private: + jobject helper_; }; } // namespace internal diff --git a/gma/src/common/ump/consent_info.cc b/gma/src/common/ump/consent_info.cc index 89f2ed0410..49b3d39f10 100644 --- a/gma/src/common/ump/consent_info.cc +++ b/gma/src/common/ump/consent_info.cc @@ -56,8 +56,7 @@ ConsentInfo* ConsentInfo::GetInstance(::firebase::InitResult* init_result_out) { ConsentInfo* consent_info = new ConsentInfo(); #if FIREBASE_PLATFORM_ANDROID - InitResult result = - consent_info->Initialize(/* jni_env, activity */); // TODO(b/291622888) + InitResult result = consent_info->Initialize(jni_env, activity); #else InitResult result = consent_info->Initialize(); #endif @@ -85,11 +84,19 @@ ConsentInfo::~ConsentInfo() { s_instance_ = nullptr; } +#if FIREBASE_PLATFORM_ANDROID +InitResult ConsentInfo::Initialize(JNIEnv* jni_env, jobject activity) { + FIREBASE_ASSERT(!internal_); + internal_ = internal::ConsentInfoInternal::CreateInstance(jni_env, activity); + return internal_ ? kInitResultSuccess : kInitResultFailedMissingDependency; +} +#else InitResult ConsentInfo::Initialize() { FIREBASE_ASSERT(!internal_); internal_ = internal::ConsentInfoInternal::CreateInstance(); return kInitResultSuccess; } +#endif // Below this, everything is a passthrough to ConsentInfoInternal. If there is // no internal_ pointer (e.g. it's been cleaned up), return default values and diff --git a/gma/src/common/ump/consent_info_internal.h b/gma/src/common/ump/consent_info_internal.h index 3438526ef0..9897e01335 100644 --- a/gma/src/common/ump/consent_info_internal.h +++ b/gma/src/common/ump/consent_info_internal.h @@ -22,6 +22,11 @@ #include "firebase/future.h" #include "firebase/gma/ump.h" #include "firebase/gma/ump/types.h" +#include "firebase/internal/platform.h" + +#if FIREBASE_PLATFORM_ANDROID +#include +#endif namespace firebase { namespace gma { @@ -44,7 +49,11 @@ class ConsentInfoInternal { // Implemented in platform-specific code to instantiate a // platform-specific subclass. +#if FIREBASE_PLATFORM_ANDROID + static ConsentInfoInternal* CreateInstance(JNIEnv* jni_env, jobject activity); +#else static ConsentInfoInternal* CreateInstance(); +#endif virtual ConsentStatus GetConsentStatus() const = 0; virtual ConsentFormStatus GetConsentFormStatus() const = 0; diff --git a/gma/src/include/firebase/gma/ump/consent_info.h b/gma/src/include/firebase/gma/ump/consent_info.h index 32abf089f8..c092f6f8f1 100644 --- a/gma/src/include/firebase/gma/ump/consent_info.h +++ b/gma/src/include/firebase/gma/ump/consent_info.h @@ -202,8 +202,7 @@ class ConsentInfo { private: ConsentInfo(); #if FIREBASE_PLATFORM_ANDROID - // TODO(b/291622888) Implement Android-specific Initialize.. - InitResult Initialize(/* JNIEnv* jni_env, jobject activity */); + InitResult Initialize(JNIEnv* jni_env, jobject activity); #else InitResult Initialize(); #endif // FIREBASE_PLATFORM_ANDROID From 54f4054edea7ee328531355d154d743f50807555 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 20:53:47 -0700 Subject: [PATCH 13/56] Lint. --- gma/src/android/ump/consent_info_internal_android.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index a62a28f494..aae66b35dc 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -16,6 +16,8 @@ #include "gma/src/android/ump/consent_info_internal_android.h" +#include + #include "app/src/thread.h" #include "app/src/util_android.h" @@ -29,7 +31,7 @@ namespace internal { X(Constructor, "", "(JLandroid/app/Activity;)V"), \ X(GetConsentStatus, "getConsentStatus", "()I"), \ X(GetConsentStatus, "requestConsentStatusUpdate", \ - "(ZILjava/util/ArrayList;)V"), \ + "(ZILjava/util/ArrayList;)V"), \ X(Disconnect, "disconnect", "()V") // clang-format on From acf9ba1196a7e91e079562e19eac0d7c67abc278 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 20:54:35 -0700 Subject: [PATCH 14/56] Lint. --- .../src/android/android_firebase_test_framework.cc | 2 +- testing/test_framework/src/firebase_test_framework.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/test_framework/src/android/android_firebase_test_framework.cc b/testing/test_framework/src/android/android_firebase_test_framework.cc index a935e5531e..7725db2c9f 100644 --- a/testing/test_framework/src/android/android_firebase_test_framework.cc +++ b/testing/test_framework/src/android/android_firebase_test_framework.cc @@ -299,7 +299,7 @@ std::string FirebaseTest::GetDebugDeviceId() { return ""; } device_id = new char[strlen(device_id_text) + 1]; - strcpy(device_id, device_id_text); + strcpy(device_id, device_id_text); // NOLINT env->ReleaseStringUTFChars(device_id_jstring, device_id_text); env->DeleteLocalRef(device_id_jstring); diff --git a/testing/test_framework/src/firebase_test_framework.h b/testing/test_framework/src/firebase_test_framework.h index 80327f0a74..83adf0fc95 100644 --- a/testing/test_framework/src/firebase_test_framework.h +++ b/testing/test_framework/src/firebase_test_framework.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "app_framework.h" // NOLINT From cdfc325eb7140ef4fdcfba17751b8669c07c5016 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 21:05:29 -0700 Subject: [PATCH 15/56] Move UMP resources to their own resource file and class path. --- gma/CMakeLists.txt | 9 +++ gma/gma_resources/build.gradle | 1 - .../ump/consent_info_internal_android.cc | 7 ++- .../internal/cpp/ConsentInfoHelper.java | 2 +- gma/ump_resources/AndroidManifest.xml | 6 ++ gma/ump_resources/build.gradle | 56 +++++++++++++++++++ 6 files changed, 76 insertions(+), 5 deletions(-) rename gma/src_java/com/google/firebase/{gma => ump}/internal/cpp/ConsentInfoHelper.java (99%) create mode 100644 gma/ump_resources/AndroidManifest.xml create mode 100644 gma/ump_resources/build.gradle diff --git a/gma/CMakeLists.txt b/gma/CMakeLists.txt index 9d63dd55de..60005fdf45 100644 --- a/gma/CMakeLists.txt +++ b/gma/CMakeLists.txt @@ -37,9 +37,18 @@ binary_to_array("gma_resources" "firebase_gma" "${FIREBASE_GEN_FILE_DIR}/gma") +# Define the resource build needed for Android +firebase_cpp_gradle(":gma:ump_resources:generateDexJarRelease" + "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar") +binary_to_array("ump_resources" + "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar" + "firebase_ump" + "${FIREBASE_GEN_FILE_DIR}/ump") + # Source files used by the Android implementation. set(android_SRCS ${gma_resources_source} + ${ump_resources_source} src/android/ump/consent_info_internal_android.cc src/android/ad_request_converter.cc src/android/ad_error_android.cc diff --git a/gma/gma_resources/build.gradle b/gma/gma_resources/build.gradle index 4cd4c54af4..79dc1bb0a8 100644 --- a/gma/gma_resources/build.gradle +++ b/gma/gma_resources/build.gradle @@ -48,7 +48,6 @@ dependencies { implementation platform('com.google.firebase:firebase-bom:32.2.2') implementation 'com.google.firebase:firebase-analytics' implementation 'com.google.android.gms:play-services-ads:22.2.0' - implementation 'com.google.android.ump:user-messaging-platform:2.1.0' } afterEvaluate { diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index aae66b35dc..695af12a5d 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -20,6 +20,7 @@ #include "app/src/thread.h" #include "app/src/util_android.h" +#include "gma/ump_resources.h" namespace firebase { namespace gma { @@ -62,9 +63,9 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* jni_env, const std::vector embedded_files = util::CacheEmbeddedFiles(env, activity, firebase::internal::EmbeddedFile::ToVector( - firebase_gma::gma_resources_filename, - firebase_gma::gma_resources_data, - firebase_gma::gma_resources_size)); + firebase_ump::ump_resources_filename, + firebase_ump::ump_resources_data, + firebase_ump::ump_resources_size)); if (!consent_info_helper::CacheClassFromFiles(env, activity, &embedded_files) != nullptr && consent_info_helper::CacheMethodIds(env, activity)) { diff --git a/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java similarity index 99% rename from gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java rename to gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java index 4b606927e4..7d53dcd38c 100644 --- a/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.firebase.gma.internal.cpp; +package com.google.firebase.ump.internal.cpp; import android.app.Activity; import android.content.Context; diff --git a/gma/ump_resources/AndroidManifest.xml b/gma/ump_resources/AndroidManifest.xml new file mode 100644 index 0000000000..02feeb8dfa --- /dev/null +++ b/gma/ump_resources/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/gma/ump_resources/build.gradle b/gma/ump_resources/build.gradle new file mode 100644 index 0000000000..75e3153e8c --- /dev/null +++ b/gma/ump_resources/build.gradle @@ -0,0 +1,56 @@ +// Copyright 2021 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. + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.google.gms:google-services:4.2.0' + } +} +allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 28 + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java { + srcDirs = ['../src_java/com/google/firebase/ump/internal/cpp'] + } + } + } +} + +dependencies { + implementation 'com.google.android.ump:user-messaging-platform:2.1.0' +} + +afterEvaluate { + generateReleaseBuildConfig.enabled = false +} + +apply from: "$rootDir/android_build_files/extract_and_dex.gradle" +extractAndDexAarFile('ump_resources') From 971a00ccb8b26867f6d8e743ed1168c29ffc1ad6 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 21:29:01 -0700 Subject: [PATCH 16/56] Fix relocated UMP resources. Add lint skip (like App Check). --- gma/build.gradle | 1 + gma/integration_test/build.gradle | 3 +++ settings.gradle | 1 + 3 files changed, 5 insertions(+) diff --git a/gma/build.gradle b/gma/build.gradle index c0c2687bd1..1df99b05b5 100644 --- a/gma/build.gradle +++ b/gma/build.gradle @@ -85,4 +85,5 @@ apply from: "$rootDir/android_build_files/generate_proguard.gradle" project.afterEvaluate { generateProguardFile('gma') setupDexDependencies(':gma:gma_resources') + setupDexDependencies(':gma:ump_resources') } diff --git a/gma/integration_test/build.gradle b/gma/integration_test/build.gradle index bbbfd2577d..de93c89ab5 100644 --- a/gma/integration_test/build.gradle +++ b/gma/integration_test/build.gradle @@ -73,6 +73,9 @@ android { proguardFile file('proguard.pro') } } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/settings.gradle b/settings.gradle index 0ec81050fc..debbf0555f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,6 +16,7 @@ include ':app', ':functions', ':gma', ':gma:gma_resources', + ':gma:ump_resources', ':installations', ':messaging', ':messaging:messaging_java', From c7e5169776fefefdd30e4102d3fee9dcb077bc17 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 23:05:11 -0700 Subject: [PATCH 17/56] Add enum conversion and fix build. --- gma/CMakeLists.txt | 2 +- gma/integration_test/src/integration_test.cc | 11 +- .../ump/consent_info_internal_android.cc | 214 ++++++++++++++++-- .../ump/consent_info_internal_android.h | 7 +- gma/src/common/ump/consent_info_internal.h | 4 +- 5 files changed, 211 insertions(+), 27 deletions(-) diff --git a/gma/CMakeLists.txt b/gma/CMakeLists.txt index 60005fdf45..1ca15484b1 100644 --- a/gma/CMakeLists.txt +++ b/gma/CMakeLists.txt @@ -43,7 +43,7 @@ firebase_cpp_gradle(":gma:ump_resources:generateDexJarRelease" binary_to_array("ump_resources" "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar" "firebase_ump" - "${FIREBASE_GEN_FILE_DIR}/ump") + "${FIREBASE_GEN_FILE_DIR}/gma") # Source files used by the Android implementation. set(android_SRCS diff --git a/gma/integration_test/src/integration_test.cc b/gma/integration_test/src/integration_test.cc index 7143237873..56bcc21b4f 100644 --- a/gma/integration_test/src/integration_test.cc +++ b/gma/integration_test/src/integration_test.cc @@ -351,6 +351,8 @@ void FirebaseGmaPreInitializationTests::SetUpTestSuite() { // Test cases below. +#if 0 // jsimantov + TEST_F(FirebaseGmaMinimalTest, TestInitializeGmaWithoutFirebase) { // Don't initialize mediation in this test so that a later test can still // verify that mediation has not been initialized. @@ -2470,6 +2472,8 @@ TEST_F(FirebaseGmaTest, TestAdViewMultithreadDeletion) { #endif // #if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && // TARGET_OS_IPHONE) +#endif // jsimantov + class FirebaseGmaUmpTest : public FirebaseGmaTest { public: FirebaseGmaUmpTest() : consent_info_(nullptr) {} @@ -2490,7 +2494,12 @@ class FirebaseGmaUmpTest : public FirebaseGmaTest { void FirebaseGmaUmpTest::InitializeUmp(ResetOption reset) { using firebase::gma::ump::ConsentInfo; - consent_info_ = ConsentInfo::GetInstance(*shared_app_); + firebase::InitResult result; + consent_info_ = ConsentInfo::GetInstance(*shared_app_, &result); + + EXPECT_NE(consent_info_, nullptr); + EXPECT_EQ(result, firebase::kInitResultSuccess); + if (consent_info_ != nullptr && reset == kReset) { consent_info_->Reset(); } diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 695af12a5d..033fc3a9e6 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -28,11 +28,11 @@ namespace ump { namespace internal { // clang-format off -#define CONSENTINFOHELPER_METHODS(X) \ - X(Constructor, "", "(JLandroid/app/Activity;)V"), \ - X(GetConsentStatus, "getConsentStatus", "()I"), \ - X(GetConsentStatus, "requestConsentStatusUpdate", \ - "(ZILjava/util/ArrayList;)V"), \ +#define CONSENTINFOHELPER_METHODS(X) \ + X(Constructor, "", "(JLandroid/app/Activity;)V"), \ + X(GetConsentStatus, "getConsentStatus", "()I"), \ + X(RequestConsentStatusUpdate, "requestConsentStatusUpdate", \ + "(ZILjava/util/ArrayList;)V"), \ X(Disconnect, "disconnect", "()V") // clang-format on @@ -42,23 +42,93 @@ METHOD_LOOKUP_DEFINITION( "com/google/firebase/gma/internal/cpp/ConsentInfoHelper", CONSENTINFOHELPER_METHODS); +// clang-format off +#define CONSENTINFORMATION_CONSENTSTATUS_FIELDS(X) \ + X(Unknown, "UNKNOWN", "I", util::kFieldTypeStatic), \ + X(NotRequired, "NOT_REQUIRED", "I", util::kFieldTypeStatic), \ + X(Required, "REQUIRED", "I", util::kFieldTypeStatic), \ + X(Obtained, "OBTAINED", "I", util::kFieldTypeStatic) +// clang-format on + +METHOD_LOOKUP_DECLARATION(consentinformation_consentstatus, METHOD_LOOKUP_NONE, + CONSENTINFORMATION_CONSENTSTATUS_FIELDS); +METHOD_LOOKUP_DEFINITION( + consentinformation_consentstatus, + PROGUARD_KEEP_CLASS + "com/google/android/ump/ConsentInformation$ConsentStatus", + METHOD_LOOKUP_NONE, CONSENTINFORMATION_CONSENTSTATUS_FIELDS); + +// clang-format off +#define FORMERROR_ERRORCODE_FIELDS(X) \ + X(InternalError, "INTERNAL_ERROR", "I", util::kFieldTypeStatic), \ + X(InternetError, "INTERNET_ERROR", "I", util::kFieldTypeStatic), \ + X(InvalidOperation, "INVALID_OPERATION", "I", util::kFieldTypeStatic), \ + X(TimeOut, "TIME_OUT", "I", util::kFieldTypeStatic) +// clang-format on + +METHOD_LOOKUP_DECLARATION(formerror_errorcode, METHOD_LOOKUP_NONE, + FORMERROR_ERRORCODE_FIELDS); +METHOD_LOOKUP_DEFINITION(formerror_errorcode, + PROGUARD_KEEP_CLASS + "com/google/android/ump/FormError$ErrorCode", + METHOD_LOOKUP_NONE, FORMERROR_ERRORCODE_FIELDS); + +// clang-format off +#define CONSENTDEBUGSETTINGS_DEBUGGEOGRAPHY_FIELDS(X) \ + X(Disabled, "DEBUG_GEOGRAPHY_DISABLED", "I", util::kFieldTypeStatic), \ + X(EEA, "DEBUG_GEOGRAPHY_EEA", "I", util::kFieldTypeStatic), \ + X(NotEEA, "DEBUG_GEOGRAPHY_NOT_EEA", "I", util::kFieldTypeStatic) +// clang-format on + +METHOD_LOOKUP_DECLARATION(consentdebugsettings_debuggeography, + METHOD_LOOKUP_NONE, + CONSENTDEBUGSETTINGS_DEBUGGEOGRAPHY_FIELDS); +METHOD_LOOKUP_DEFINITION( + consentdebugsettings_debuggeography, + PROGUARD_KEEP_CLASS + "com/google/android/ump/ConsentDebugSettings$DebugGeography", + METHOD_LOOKUP_NONE, CONSENTDEBUGSETTINGS_DEBUGGEOGRAPHY_FIELDS); + // This explicitly implements the constructor for the outer class, // ConsentInfoInternal. ConsentInfoInternal* ConsentInfoInternal::CreateInstance(JNIEnv* jni_env, jobject activity) { - ConsentInfoInternalAndroid ptr = + ConsentInfoInternalAndroid* ptr = new ConsentInfoInternalAndroid(jni_env, activity); - if (!ptr.valid()) { + if (!ptr->valid()) { delete ptr; return nullptr; } return ptr; } -ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* jni_env, +static void ReleaseClasses(JNIEnv* env) { + consent_info_helper::ReleaseClass(env); + consentinformation_consentstatus::ReleaseClass(env); + formerror_errorcode::ReleaseClass(env); + consentdebugsettings_debuggeography::ReleaseClass(env); +} + +ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { + JNIEnv* env = GetJNIEnv(); + env->CallVoidMethod(helper_, consent_info_helper::GetMethodId( + consent_info_helper::kDisconnect)); + env->DeleteGlobalRef(helper_); + helper_ = nullptr; + + ReleaseClasses(env); + util::Terminate(env); + + env->DeleteGlobalRef(activity_); + activity_ = nullptr; + java_vm_ = nullptr; +} + +ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, jobject activity) - : helper_(nullptr) { - util::Initialize(jni_env, activity); + : java_vm_(nullptr), activity_(nullptr), helper_(nullptr) { + util::Initialize(env, activity); + env->GetJavaVM(&java_vm_); const std::vector embedded_files = util::CacheEmbeddedFiles(env, activity, @@ -66,29 +136,117 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* jni_env, firebase_ump::ump_resources_filename, firebase_ump::ump_resources_data, firebase_ump::ump_resources_size)); - if (!consent_info_helper::CacheClassFromFiles(env, activity, - &embedded_files) != nullptr && - consent_info_helper::CacheMethodIds(env, activity)) { - util::Terminate(); + if (!(consent_info_helper::CacheClassFromFiles(env, activity, + &embedded_files) != nullptr && + consent_info_helper::CacheMethodIds(env, activity) && + consentinformation_consentstatus::CacheFieldIds(env, activity) && + formerror_errorcode::CacheFieldIds(env, activity) && + consentdebugsettings_debuggeography::CacheFieldIds(env, activity))) { + ReleaseClasses(env); + util::Terminate(env); return; } jobject helper_ref = env->NewObject( consent_info_helper::GetClass(), - consent_info_helper::GetMethodId(rewarded_ad_helper::kConstructor), + consent_info_helper::GetMethodId(consent_info_helper::kConstructor), reinterpret_cast(this)); util::CheckAndClearJniExceptions(env); + if (!helper_ref) { + ReleaseClasses(env); + util::Terminate(env); + return; + } - FIREBASE_ASSERT(helper_ref); helper_ = env->NewGlobalRef(helper_ref); FIREBASE_ASSERT(helper_); env->DeleteLocalRef(helper_ref); + + activity_ = env->NewGlobalRef(activity); + + util::CheckAndClearJniExceptions(env); } -ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { - env->DeleteGlobalRef(helper_); - helper_ = nullptr; - consent_info_helper::Terminate(); - util::Terminate(); +// clang-format off +#define ENUM_VALUE(class_namespace, field_name) \ + env->GetStaticIntField(class_namespace::GetClass(), \ + class_namespace::GetFieldId(class_namespace::k##field_name)) +// clang-format on + +static ConsentStatus CppConsentStatusFromAndroidConsentStatus(JNIEnv* env, + jint status) { + static jint status_unknown = + ENUM_VALUE(consentinformation_consentstatus, Unknown); + static jint status_required = + ENUM_VALUE(consentinformation_consentstatus, Required); + static jint status_not_required = + ENUM_VALUE(consentinformation_consentstatus, NotRequired); + static jint status_obtained = + ENUM_VALUE(consentinformation_consentstatus, Obtained); + if (status == status_unknown) return kConsentStatusUnknown; + if (status == status_required) return kConsentStatusRequired; + if (status == status_not_required) return kConsentStatusNotRequired; + if (status == status_obtained) return kConsentStatusObtained; + return kConsentStatusUnknown; +} + +static bool MessageContains(JNIEnv* env, jstring message, const char* text) { + if (!message) return false; + std::string message_str = util::JStringToString(env, message); + return (message_str.find(text) != std::string::npos); +} + +static jint AndroidDebugGeographyFromCppDebugGeography( + JNIEnv* env, ConsentDebugGeography geo) { + static jint geo_disabled = + ENUM_VALUE(consentdebugsettings_debuggeography, Disabled); + static jint geo_eea = ENUM_VALUE(consentdebugsettings_debuggeography, EEA); + static jint geo_not_eea = + ENUM_VALUE(consentdebugsettings_debuggeography, NotEEA); + switch (geo) { + case kConsentDebugGeographyDisabled: + return geo_disabled; + case kConsentDebugGeographyEEA: + return geo_eea; + case kConsentDebugGeographyNonEEA: + return geo_not_eea; + default: + return geo_disabled; + } +} + +static ConsentRequestError CppConsentRequestErrorFromAndroidFormError( + JNIEnv* env, jint error, jstring message = nullptr) { + static jint error_success = 0; + static jint error_internal = ENUM_VALUE(formerror_errorcode, InternalError); + static jint error_network = ENUM_VALUE(formerror_errorcode, InternetError); + static jint error_invalid_operation = + ENUM_VALUE(formerror_errorcode, InvalidOperation); + + if (error == error_success) return kConsentRequestSuccess; + if (error == error_internal) return kConsentRequestErrorInternal; + if (error == error_network) return kConsentRequestErrorNetwork; + if (error == error_invalid_operation) + return kConsentRequestErrorInvalidOperation; + return kConsentRequestErrorUnknown; +} + +static ConsentFormError CppConsentFormErrorFromAndroidFormError( + JNIEnv* env, jint error, jstring message = nullptr) { + static jint error_success = 0; + static jint error_internal = ENUM_VALUE(formerror_errorcode, InternalError); + static jint error_invalid_operation = + ENUM_VALUE(formerror_errorcode, InvalidOperation); + static jint error_timeout = ENUM_VALUE(formerror_errorcode, TimeOut); + if (error == error_success) return kConsentFormSuccess; + if (error == error_internal) return kConsentFormErrorInternal; + if (error == error_timeout) return kConsentFormErrorTimeout; + if (error == error_invalid_operation) { + if (MessageContains(env, message, "unavailable")) + return kConsentFormErrorUnavailable; + else + return kConsentFormErrorInvalidOperation; + } + return kConsentFormErrorUnknown; } Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( @@ -101,7 +259,14 @@ Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( } ConsentStatus ConsentInfoInternalAndroid::GetConsentStatus() { - return kConsentStatusUnknown; + if (!valid()) { + return kConsentStatusUnknown; + } + JNIEnv* env = GetJNIEnv(); + jint result = env->CallIntMethod( + helper_, + consent_info_helper::GetMethodId(consent_info_helper::kGetConsentStatus)); + return CppConsentStatusFromAndroidConsentStatus(env, result); } ConsentFormStatus ConsentInfoInternalAndroid::GetConsentFormStatus() { return kConsentFormStatusUnknown; @@ -148,6 +313,11 @@ bool ConsentInfoInternalAndroid::CanRequestAds() { return false; } void ConsentInfoInternalAndroid::Reset() {} +JNIEnv* ConsentInfoInternalAndroid::GetJNIEnv() { + return firebase::util::GetThreadsafeJNIEnv(java_vm_); +} +jobject ConsentInfoInternalAndroid::activity() { return activity_; } + } // namespace internal } // namespace ump } // namespace gma diff --git a/gma/src/android/ump/consent_info_internal_android.h b/gma/src/android/ump/consent_info_internal_android.h index e5e7c6e86b..5a234a964c 100644 --- a/gma/src/android/ump/consent_info_internal_android.h +++ b/gma/src/android/ump/consent_info_internal_android.h @@ -27,7 +27,7 @@ namespace internal { class ConsentInfoInternalAndroid : public ConsentInfoInternal { public: - ConsentInfoInternalAndroid(); + ConsentInfoInternalAndroid(JNIEnv* env, jobject activity); ~ConsentInfoInternalAndroid() override; ConsentStatus GetConsentStatus() override; @@ -49,7 +49,12 @@ class ConsentInfoInternalAndroid : public ConsentInfoInternal { bool valid() { return (helper_ != nullptr); } + JNIEnv* GetJNIEnv(); + jobject activity(); + private: + JavaVM* java_vm_; + jobject activity_; jobject helper_; }; diff --git a/gma/src/common/ump/consent_info_internal.h b/gma/src/common/ump/consent_info_internal.h index 9897e01335..94d87a5ec0 100644 --- a/gma/src/common/ump/consent_info_internal.h +++ b/gma/src/common/ump/consent_info_internal.h @@ -55,8 +55,8 @@ class ConsentInfoInternal { static ConsentInfoInternal* CreateInstance(); #endif - virtual ConsentStatus GetConsentStatus() const = 0; - virtual ConsentFormStatus GetConsentFormStatus() const = 0; + virtual ConsentStatus GetConsentStatus() = 0; + virtual ConsentFormStatus GetConsentFormStatus() = 0; virtual Future RequestConsentInfoUpdate( const ConsentRequestParameters& params) = 0; From 4d0db12af1e31924c05a6e8fa5250b93dc14585b Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 16 Aug 2023 23:15:27 -0700 Subject: [PATCH 18/56] Add enum cache. --- .../ump/consent_info_internal_android.cc | 111 +++++++++++------- 1 file changed, 66 insertions(+), 45 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 033fc3a9e6..4b8574ad04 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -124,6 +124,29 @@ ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { java_vm_ = nullptr; } +// clang-format off +#define ENUM_VALUE(class_namespace, field_name) \ + env->GetStaticIntField(class_namespace::GetClass(), \ + class_namespace::GetFieldId(class_namespace::k##field_name)) +// clang-format on + +static struct { + jint consentstatus_unknown; + jint consentstatus_required; + jint consentstatus_not_required; + jint consentstatus_obtained; + + jint formerror_success; + jint formerror_internal; + jint formerror_network; + jint formerror_invalid_operation; + jint formerror_timeout; + + jint debuggeo_disabled; + jint debuggeo_eea; + jint debuggeo_not_eea; +} g_enum_cache; + ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, jobject activity) : java_vm_(nullptr), activity_(nullptr), helper_(nullptr) { @@ -164,28 +187,40 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, activity_ = env->NewGlobalRef(activity); util::CheckAndClearJniExceptions(env); -} - -// clang-format off -#define ENUM_VALUE(class_namespace, field_name) \ - env->GetStaticIntField(class_namespace::GetClass(), \ - class_namespace::GetFieldId(class_namespace::k##field_name)) -// clang-format on -static ConsentStatus CppConsentStatusFromAndroidConsentStatus(JNIEnv* env, - jint status) { - static jint status_unknown = + // Cache enum values when the class loads. + g_enum_cache.consentstatus_unknown = ENUM_VALUE(consentinformation_consentstatus, Unknown); - static jint status_required = + g_enum_cache.consentstatus_required = ENUM_VALUE(consentinformation_consentstatus, Required); - static jint status_not_required = + g_enum_cache.consentstatus_not_required = ENUM_VALUE(consentinformation_consentstatus, NotRequired); - static jint status_obtained = + g_enum_cache.consentstatus_obtained = ENUM_VALUE(consentinformation_consentstatus, Obtained); - if (status == status_unknown) return kConsentStatusUnknown; - if (status == status_required) return kConsentStatusRequired; - if (status == status_not_required) return kConsentStatusNotRequired; - if (status == status_obtained) return kConsentStatusObtained; + + g_enum_cache.debuggeo_disabled = + ENUM_VALUE(consentdebugsettings_debuggeography, Disabled); + g_enum_cache.debuggeo_eea = + ENUM_VALUE(consentdebugsettings_debuggeography, EEA); + g_enum_cache.debuggeo_not_eea = + ENUM_VALUE(consentdebugsettings_debuggeography, NotEEA); + + g_enum_cache.formerror_success = 0; + g_enum_cache.formerror_internal = ENUM_VALUE(formerror_errorcode, InternalError); + g_enum_cache.formerror_network = ENUM_VALUE(formerror_errorcode, InternetError); + g_enum_cache.formerror_invalid_operation = + ENUM_VALUE(formerror_errorcode, InvalidOperation); + g_enum_cache.formerror_timeout = ENUM_VALUE(formerror_errorcode, TimeOut); + + util::CheckAndClearJniExceptions(env); +} + +static ConsentStatus CppConsentStatusFromAndroidConsentStatus(JNIEnv* env, + jint status) { + if (status == g_enum_cache.consentstatus_unknown) return kConsentStatusUnknown; + if (status == g_enum_cache.consentstatus_required) return kConsentStatusRequired; + if (status == g_enum_cache.consentstatus_not_required) return kConsentStatusNotRequired; + if (status == g_enum_cache.consentstatus_obtained) return kConsentStatusObtained; return kConsentStatusUnknown; } @@ -197,50 +232,36 @@ static bool MessageContains(JNIEnv* env, jstring message, const char* text) { static jint AndroidDebugGeographyFromCppDebugGeography( JNIEnv* env, ConsentDebugGeography geo) { - static jint geo_disabled = - ENUM_VALUE(consentdebugsettings_debuggeography, Disabled); - static jint geo_eea = ENUM_VALUE(consentdebugsettings_debuggeography, EEA); - static jint geo_not_eea = - ENUM_VALUE(consentdebugsettings_debuggeography, NotEEA); + // Cache values the first time this function runs. switch (geo) { case kConsentDebugGeographyDisabled: - return geo_disabled; + return g_enum_cache.debuggeo_disabled; case kConsentDebugGeographyEEA: - return geo_eea; + return g_enum_cache.debuggeo_eea; case kConsentDebugGeographyNonEEA: - return geo_not_eea; + return g_enum_cache.debuggeo_not_eea; default: - return geo_disabled; + return g_enum_cache.debuggeo_disabled; } } static ConsentRequestError CppConsentRequestErrorFromAndroidFormError( JNIEnv* env, jint error, jstring message = nullptr) { - static jint error_success = 0; - static jint error_internal = ENUM_VALUE(formerror_errorcode, InternalError); - static jint error_network = ENUM_VALUE(formerror_errorcode, InternetError); - static jint error_invalid_operation = - ENUM_VALUE(formerror_errorcode, InvalidOperation); - - if (error == error_success) return kConsentRequestSuccess; - if (error == error_internal) return kConsentRequestErrorInternal; - if (error == error_network) return kConsentRequestErrorNetwork; - if (error == error_invalid_operation) + // Cache values the first time this function runs. + if (error == g_enum_cache.formerror_success) return kConsentRequestSuccess; + if (error == g_enum_cache.formerror_internal) return kConsentRequestErrorInternal; + if (error == g_enum_cache.formerror_network) return kConsentRequestErrorNetwork; + if (error == g_enum_cache.formerror_invalid_operation) return kConsentRequestErrorInvalidOperation; return kConsentRequestErrorUnknown; } static ConsentFormError CppConsentFormErrorFromAndroidFormError( JNIEnv* env, jint error, jstring message = nullptr) { - static jint error_success = 0; - static jint error_internal = ENUM_VALUE(formerror_errorcode, InternalError); - static jint error_invalid_operation = - ENUM_VALUE(formerror_errorcode, InvalidOperation); - static jint error_timeout = ENUM_VALUE(formerror_errorcode, TimeOut); - if (error == error_success) return kConsentFormSuccess; - if (error == error_internal) return kConsentFormErrorInternal; - if (error == error_timeout) return kConsentFormErrorTimeout; - if (error == error_invalid_operation) { + if (error == g_enum_cache.formerror_success) return kConsentFormSuccess; + if (error == g_enum_cache.formerror_internal) return kConsentFormErrorInternal; + if (error == g_enum_cache.formerror_timeout) return kConsentFormErrorTimeout; + if (error == g_enum_cache.formerror_invalid_operation) { if (MessageContains(env, message, "unavailable")) return kConsentFormErrorUnavailable; else From a1208709731029286a62254f59a3319653934351 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 13:26:31 -0700 Subject: [PATCH 19/56] Android implementation of ConsentInfo functionality. --- app/src/util_android.cc | 9 + app/src/util_android.h | 6 + .../ump/consent_info_internal_android.cc | 328 +++++++++++++++--- .../ump/consent_info_internal_android.h | 22 ++ .../ump/internal/cpp/ConsentInfoHelper.java | 179 ++++++++-- 5 files changed, 466 insertions(+), 78 deletions(-) diff --git a/app/src/util_android.cc b/app/src/util_android.cc index 1ece3f1797..c052a8e7e0 100644 --- a/app/src/util_android.cc +++ b/app/src/util_android.cc @@ -1804,6 +1804,15 @@ std::string GetAndClearExceptionMessage(JNIEnv* env) { return std::string(); } +bool HasExceptionOccurred(JNIEnv* env) { + jobject exception = env->ExceptionOccurred(); + if (exception) { + env->DeleteLocalRef(exception); + return true; + } + return false; +} + std::string GetMessageFromException(JNIEnv* env, jobject exception) { if (exception != nullptr) { jstring message = static_cast(env->CallObjectMethod( diff --git a/app/src/util_android.h b/app/src/util_android.h index 722e81bbe0..4102ea872c 100644 --- a/app/src/util_android.h +++ b/app/src/util_android.h @@ -614,6 +614,8 @@ METHOD_LOOKUP_DECLARATION(double_class, DOUBLE_METHODS); #define ENUM_METHODS(X) \ X(Equals, "equals", \ "(Ljava/lang/Object;)Z"), \ + X(Ordinal, "ordinal", \ + "()I"), \ X(Name, "name", \ "()Ljava/lang/String;") // clang-format on METHOD_LOOKUP_DECLARATION(enum_class, ENUM_METHODS); @@ -1094,6 +1096,10 @@ bool CheckAndClearJniExceptions(JNIEnv* env); // Otherwise, it will return an empty string. std::string GetAndClearExceptionMessage(JNIEnv* env); +// Returns true if an JNI exception occurred, false otherwise. +// Does not clear the exception, so you can then call GetAndClearExceptionMessage(). +bool HasExceptionOccurred(JNIEnv* env); + // Returns the message from an exception. // This does not clear the JNI exception state. std::string GetMessageFromException(JNIEnv* env, jobject exception); diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 4b8574ad04..6070fb8f20 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -16,10 +16,14 @@ #include "gma/src/android/ump/consent_info_internal_android.h" +#include + #include +#include "app/src/assert.h" #include "app/src/thread.h" #include "app/src/util_android.h" +#include "firebase/internal/common.h" #include "gma/ump_resources.h" namespace firebase { @@ -27,20 +31,46 @@ namespace gma { namespace ump { namespace internal { +ConsentInfoInternalAndroid* ConsentInfoInternalAndroid::s_instance = nullptr; +firebase::Mutex ConsentInfoInternalAndroid::s_instance_mutex; + // clang-format off #define CONSENTINFOHELPER_METHODS(X) \ X(Constructor, "", "(JLandroid/app/Activity;)V"), \ X(GetConsentStatus, "getConsentStatus", "()I"), \ - X(RequestConsentStatusUpdate, "requestConsentStatusUpdate", \ + X(RequestConsentInfoUpdate, "requestConsentInfoUpdate", \ "(ZILjava/util/ArrayList;)V"), \ + X(LoadConsentForm, "loadConsentForm", "(Z)V"), \ + X(ShowConsentForm, "showConsentForm", "(ZLandroid/app/Activity;)Z"), \ + X(LoadAndShowConsentFormIfRequired, "loadAndShowConsentFormIfRequired", \ + "(ZLandroid/app/Activity;)V"), \ + X(GetPrivacyOptionsRequirementStatus, "getPrivacyOptionsRequirementStatus", \ + "()I"), \ + X(ShowPrivacyOptionsForm, "showPrivacyOptionsForm", \ + "(ZLandroid/app/Activity;)V"), \ + X(Reset, "reset", "()V"), \ + X(CanRequestAds, "canRequestAds", "()Z"), \ + X(IsConsentFormAvailable, "isConsentFormAvailable", "()Z"), \ X(Disconnect, "disconnect", "()V") // clang-format on -METHOD_LOOKUP_DECLARATION(consent_info_helper, CONSENTINFOHELPER_METHODS); +// clang-format off +#define CONSENTINFOHELPER_FIELDS(X) \ + X(PrivacyOptionsRequirementUnknown, \ + "PRIVACY_OPTIONS_REQUIREMENT_UNKNOWN", "I", util::kFieldTypeStatic), \ + X(PrivacyOptionsRequirementRequired, \ + "PRIVACY_OPTIONS_REQUIREMENT_REQUIRED", "I", util::kFieldTypeStatic), \ + X(PrivacyOptionsRequirementNotRequired, \ + "PRIVACY_OPTIONS_REQUIREMENT_NOT_REQUIRED", "I", util::kFieldTypeStatic) +// clang-format on + +METHOD_LOOKUP_DECLARATION(consent_info_helper, CONSENTINFOHELPER_METHODS, + CONSENTINFOHELPER_FIELDS); + METHOD_LOOKUP_DEFINITION( consent_info_helper, - "com/google/firebase/gma/internal/cpp/ConsentInfoHelper", - CONSENTINFOHELPER_METHODS); + "com/google/firebase/ump/internal/cpp/ConsentInfoHelper", + CONSENTINFOHELPER_METHODS, CONSENTINFOHELPER_FIELDS); // clang-format off #define CONSENTINFORMATION_CONSENTSTATUS_FIELDS(X) \ @@ -110,6 +140,8 @@ static void ReleaseClasses(JNIEnv* env) { } ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { + MutexLock lock(s_instance_mutex); + JNIEnv* env = GetJNIEnv(); env->CallVoidMethod(helper_, consent_info_helper::GetMethodId( consent_info_helper::kDisconnect)); @@ -142,14 +174,50 @@ static struct { jint formerror_invalid_operation; jint formerror_timeout; - jint debuggeo_disabled; - jint debuggeo_eea; - jint debuggeo_not_eea; + jint debug_geography_disabled; + jint debug_geography_eea; + jint debug_geography_not_eea; + + jint privacy_options_requirement_unknown; + jint privacy_options_requirement_required; + jint privacy_options_requirement_not_required; } g_enum_cache; +void ConsentInfoInternalAndroid::JNI_ConsentInfoHelper_completeFuture(JNIEnv* env, + jclass clazz, + jint future_fn, + jlong consent_info_internal_ptr, + jlong future_handle, + jint error_code, + jobject error_message_obj) { + if (consent_info_internal_ptr == 0 || future_fn < 0 || future_fn >= kConsentInfoFnCount) { + // Calling this with a null pointer or invalid fn is a no-op, so just return. + return; + } + { + MutexLock lock(s_instance_mutex); + std::string error_message = error_message_obj ? util::JniStringToString(env, error_message_obj) : ""; + ConsentInfoInternalAndroid* instance = reinterpret_cast(consent_info_internal_ptr); + if (s_instance != instance) { + // If the instance we were called with does not match the current instance, a bad race condition + // has occurred (whereby while waiting for the operation to complete, ConsentInfo was deleted and then recreated). + // In that case, fully ignore this callback. + return; + } + instance->CompleteFutureFromJniCallback(env, + static_cast(future_fn), + static_cast(future_handle), + static_cast(error_code), + error_message.length() > 0 ? error_message.c_str() : nullptr); + } +} + ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, jobject activity) - : java_vm_(nullptr), activity_(nullptr), helper_(nullptr) { + : java_vm_(nullptr), activity_(nullptr), helper_(nullptr), has_requested_consent_info_update_(false) { + MutexLock lock(s_instance_mutex); + FIREBASE_ASSERT(s_instance == nullptr); + util::Initialize(env, activity); env->GetJavaVM(&java_vm_); @@ -162,6 +230,7 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, if (!(consent_info_helper::CacheClassFromFiles(env, activity, &embedded_files) != nullptr && consent_info_helper::CacheMethodIds(env, activity) && + consent_info_helper::CacheFieldIds(env, activity) && consentinformation_consentstatus::CacheFieldIds(env, activity) && formerror_errorcode::CacheFieldIds(env, activity) && consentdebugsettings_debuggeography::CacheFieldIds(env, activity))) { @@ -169,6 +238,18 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, util::Terminate(env); return; } + static const JNINativeMethod kConsentInfoHelperNativeMethods[] = { + {"completeFuture", "(IJJILjava/lang/String;)V", + reinterpret_cast(&JNI_ConsentInfoHelper_completeFuture)} + }; + if (!consent_info_helper::RegisterNatives(env, kConsentInfoHelperNativeMethods, + FIREBASE_ARRAYSIZE(kConsentInfoHelperNativeMethods))) { + util::CheckAndClearJniExceptions(env); + ReleaseClasses(env); + util::Terminate(env); + return; + } + util::CheckAndClearJniExceptions(env); jobject helper_ref = env->NewObject( consent_info_helper::GetClass(), consent_info_helper::GetMethodId(consent_info_helper::kConstructor), @@ -188,7 +269,8 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, util::CheckAndClearJniExceptions(env); - // Cache enum values when the class loads. + // Cache enum values when the class loads, to avoid JNI lookups during + // callbacks later on when converting enums between Android and C++ values. g_enum_cache.consentstatus_unknown = ENUM_VALUE(consentinformation_consentstatus, Unknown); g_enum_cache.consentstatus_required = @@ -198,71 +280,88 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, g_enum_cache.consentstatus_obtained = ENUM_VALUE(consentinformation_consentstatus, Obtained); - g_enum_cache.debuggeo_disabled = - ENUM_VALUE(consentdebugsettings_debuggeography, Disabled); - g_enum_cache.debuggeo_eea = - ENUM_VALUE(consentdebugsettings_debuggeography, EEA); - g_enum_cache.debuggeo_not_eea = - ENUM_VALUE(consentdebugsettings_debuggeography, NotEEA); + g_enum_cache.debug_geography_disabled = + ENUM_VALUE(consentdebugsettings_debuggeography, Disabled); + g_enum_cache.debug_geography_eea = + ENUM_VALUE(consentdebugsettings_debuggeography, EEA); + g_enum_cache.debug_geography_not_eea = + ENUM_VALUE(consentdebugsettings_debuggeography, NotEEA); g_enum_cache.formerror_success = 0; - g_enum_cache.formerror_internal = ENUM_VALUE(formerror_errorcode, InternalError); - g_enum_cache.formerror_network = ENUM_VALUE(formerror_errorcode, InternetError); + g_enum_cache.formerror_internal = + ENUM_VALUE(formerror_errorcode, InternalError); + g_enum_cache.formerror_network = + ENUM_VALUE(formerror_errorcode, InternetError); g_enum_cache.formerror_invalid_operation = - ENUM_VALUE(formerror_errorcode, InvalidOperation); + ENUM_VALUE(formerror_errorcode, InvalidOperation); g_enum_cache.formerror_timeout = ENUM_VALUE(formerror_errorcode, TimeOut); - + + g_enum_cache.privacy_options_requirement_unknown = + ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementUnknown); + g_enum_cache.privacy_options_requirement_required = + ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementRequired); + g_enum_cache.privacy_options_requirement_not_required = + ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementNotRequired); + util::CheckAndClearJniExceptions(env); } -static ConsentStatus CppConsentStatusFromAndroidConsentStatus(JNIEnv* env, - jint status) { - if (status == g_enum_cache.consentstatus_unknown) return kConsentStatusUnknown; - if (status == g_enum_cache.consentstatus_required) return kConsentStatusRequired; - if (status == g_enum_cache.consentstatus_not_required) return kConsentStatusNotRequired; - if (status == g_enum_cache.consentstatus_obtained) return kConsentStatusObtained; +static ConsentStatus CppConsentStatusFromAndroid(jint status) { + if (status == g_enum_cache.consentstatus_unknown) + return kConsentStatusUnknown; + if (status == g_enum_cache.consentstatus_required) + return kConsentStatusRequired; + if (status == g_enum_cache.consentstatus_not_required) + return kConsentStatusNotRequired; + if (status == g_enum_cache.consentstatus_obtained) + return kConsentStatusObtained; return kConsentStatusUnknown; } -static bool MessageContains(JNIEnv* env, jstring message, const char* text) { - if (!message) return false; - std::string message_str = util::JStringToString(env, message); - return (message_str.find(text) != std::string::npos); +static PrivacyOptionsRequirementStatus +CppPrivacyOptionsRequirementStatusFromAndroid(jint status) { + if (status == g_enum_cache.privacy_options_requirement_unknown) + return kPrivacyOptionsRequirementStatusUnknown; + if (status == g_enum_cache.privacy_options_requirement_required) + return kPrivacyOptionsRequirementStatusRequired; + if (status == g_enum_cache.privacy_options_requirement_not_required) + return kPrivacyOptionsRequirementStatusNotRequired; + return kPrivacyOptionsRequirementStatusUnknown; } -static jint AndroidDebugGeographyFromCppDebugGeography( - JNIEnv* env, ConsentDebugGeography geo) { +static jint AndroidDebugGeographyFromCppDebugGeography(ConsentDebugGeography geo) { // Cache values the first time this function runs. switch (geo) { case kConsentDebugGeographyDisabled: - return g_enum_cache.debuggeo_disabled; + return g_enum_cache.debug_geography_disabled; case kConsentDebugGeographyEEA: - return g_enum_cache.debuggeo_eea; + return g_enum_cache.debug_geography_eea; case kConsentDebugGeographyNonEEA: - return g_enum_cache.debuggeo_not_eea; + return g_enum_cache.debug_geography_not_eea; default: - return g_enum_cache.debuggeo_disabled; + return g_enum_cache.debug_geography_disabled; } } -static ConsentRequestError CppConsentRequestErrorFromAndroidFormError( - JNIEnv* env, jint error, jstring message = nullptr) { +static ConsentRequestError CppConsentRequestErrorFromAndroidFormError(jint error, const char* message = nullptr) { // Cache values the first time this function runs. if (error == g_enum_cache.formerror_success) return kConsentRequestSuccess; - if (error == g_enum_cache.formerror_internal) return kConsentRequestErrorInternal; - if (error == g_enum_cache.formerror_network) return kConsentRequestErrorNetwork; + if (error == g_enum_cache.formerror_internal) + return kConsentRequestErrorInternal; + if (error == g_enum_cache.formerror_network) + return kConsentRequestErrorNetwork; if (error == g_enum_cache.formerror_invalid_operation) return kConsentRequestErrorInvalidOperation; return kConsentRequestErrorUnknown; } -static ConsentFormError CppConsentFormErrorFromAndroidFormError( - JNIEnv* env, jint error, jstring message = nullptr) { +static ConsentFormError CppConsentFormErrorFromAndroidFormError(jint error, const char* message = nullptr) { if (error == g_enum_cache.formerror_success) return kConsentFormSuccess; - if (error == g_enum_cache.formerror_internal) return kConsentFormErrorInternal; + if (error == g_enum_cache.formerror_internal) + return kConsentFormErrorInternal; if (error == g_enum_cache.formerror_timeout) return kConsentFormErrorTimeout; if (error == g_enum_cache.formerror_invalid_operation) { - if (MessageContains(env, message, "unavailable")) + if (message && strcasestr(message, "unavailable") != nullptr) return kConsentFormErrorUnavailable; else return kConsentFormErrorInvalidOperation; @@ -274,8 +373,27 @@ Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( const ConsentRequestParameters& params) { SafeFutureHandle handle = CreateFuture(kConsentInfoFnRequestConsentInfoUpdate); + JNIEnv* env = GetJNIEnv(); + + jlong future_handle = static_cast(handle.get().id()); + jboolean tag_for_under_age_of_consent = + static_cast(params.tag_for_under_age_of_consent); + jint debug_geography = AndroidDebugGeographyFromCppDebugGeography(params.debug_settings.debug_geography); + jobject debug_device_ids_list = + util::StdVectorToJavaList(env, params.debug_settings.debug_device_ids); + env->CallVoidMethod(helper_, + consent_info_helper::GetMethodId(consent_info_helper::kRequestConsentInfoUpdate), + future_handle, tag_for_under_age_of_consent, debug_geography, + debug_device_ids_list); + + if (util::HasExceptionOccurred(env)) { + std::string exception_message = util::GetAndClearExceptionMessage(env); + CompleteFuture(handle, kConsentRequestErrorInternal, exception_message.c_str()); + } else { + has_requested_consent_info_update_ = true; + } + env->DeleteLocalRef(debug_device_ids_list); - CompleteFuture(handle, kConsentRequestSuccess); return MakeFuture(futures(), handle); } @@ -287,23 +405,58 @@ ConsentStatus ConsentInfoInternalAndroid::GetConsentStatus() { jint result = env->CallIntMethod( helper_, consent_info_helper::GetMethodId(consent_info_helper::kGetConsentStatus)); - return CppConsentStatusFromAndroidConsentStatus(env, result); + if (util::HasExceptionOccurred(env)) { + util::CheckAndClearJniExceptions(env); + return kConsentStatusUnknown; + } + return CppConsentStatusFromAndroid(result); } + ConsentFormStatus ConsentInfoInternalAndroid::GetConsentFormStatus() { - return kConsentFormStatusUnknown; + if (!valid() || !has_requested_consent_info_update_) { + return kConsentFormStatusUnknown; + } + JNIEnv* env = GetJNIEnv(); + jboolean is_available = env->CallBooleanMethod( + helper_, + consent_info_helper::GetMethodId(consent_info_helper::kIsConsentFormAvailable)); + if (util::HasExceptionOccurred(env)) { + util::CheckAndClearJniExceptions(env); + return kConsentFormStatusUnknown; + } + return is_available ? kConsentFormStatusAvailable : kConsentFormStatusUnavailable; } Future ConsentInfoInternalAndroid::LoadConsentForm() { SafeFutureHandle handle = CreateFuture(kConsentInfoFnLoadConsentForm); - - CompleteFuture(handle, kConsentFormSuccess); + JNIEnv* env = GetJNIEnv(); + jlong future_handle = static_cast(handle.get().id()); + + env->CallVoidMethod(helper_, + consent_info_helper::GetMethodId(consent_info_helper::kLoadConsentForm), + future_handle); + if (util::HasExceptionOccurred(env)) { + std::string exception_message = util::GetAndClearExceptionMessage(env); + CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + } return MakeFuture(futures(), handle); } Future ConsentInfoInternalAndroid::ShowConsentForm(FormParent parent) { SafeFutureHandle handle = CreateFuture(kConsentInfoFnShowConsentForm); + JNIEnv* env = GetJNIEnv(); - CompleteFuture(handle, kConsentRequestSuccess); + jlong future_handle = static_cast(handle.get().id()); + jboolean success = env->CallBooleanMethod(helper_, + consent_info_helper::GetMethodId(consent_info_helper::kShowConsentForm), + future_handle, parent); + if (util::HasExceptionOccurred(env)) { + std::string exception_message = util::GetAndClearExceptionMessage(env); + CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + } else if (!success) { + CompleteFuture(handle, kConsentFormErrorUnavailable, + "The consent form is unavailable. Please call LoadConsentForm and ensure it completes successfully before calling ShowConsentForm."); + } return MakeFuture(futures(), handle); } @@ -312,13 +465,30 @@ Future ConsentInfoInternalAndroid::LoadAndShowConsentFormIfRequired( SafeFutureHandle handle = CreateFuture(kConsentInfoFnLoadAndShowConsentFormIfRequired); - CompleteFuture(handle, kConsentRequestSuccess); + JNIEnv* env = GetJNIEnv(); + jlong future_handle = static_cast(handle.get().id()); + + env->CallVoidMethod(helper_, + consent_info_helper::GetMethodId(consent_info_helper::kLoadAndShowConsentFormIfRequired), + future_handle, parent); + if (util::HasExceptionOccurred(env)) { + std::string exception_message = util::GetAndClearExceptionMessage(env); + CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + } + return MakeFuture(futures(), handle); } PrivacyOptionsRequirementStatus ConsentInfoInternalAndroid::GetPrivacyOptionsRequirementStatus() { - return kPrivacyOptionsRequirementStatusUnknown; + if (!valid()) { + return kPrivacyOptionsRequirementStatusUnknown; + } + JNIEnv* env = GetJNIEnv(); + jint result = env->CallIntMethod( + helper_, consent_info_helper::GetMethodId( + consent_info_helper::kGetPrivacyOptionsRequirementStatus)); + return CppPrivacyOptionsRequirementStatusFromAndroid(result); } Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( @@ -326,19 +496,69 @@ Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( SafeFutureHandle handle = CreateFuture(kConsentInfoFnShowPrivacyOptionsForm); - CompleteFuture(handle, kConsentRequestSuccess); + JNIEnv* env = GetJNIEnv(); + jlong future_handle = static_cast(handle.get().id()); + + env->CallVoidMethod(helper_, + consent_info_helper::GetMethodId(consent_info_helper::kShowPrivacyOptionsForm), + future_handle, parent); + if (util::HasExceptionOccurred(env)) { + std::string exception_message = util::GetAndClearExceptionMessage(env); + CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + } + return MakeFuture(futures(), handle); } -bool ConsentInfoInternalAndroid::CanRequestAds() { return false; } +bool ConsentInfoInternalAndroid::CanRequestAds() { + JNIEnv* env = GetJNIEnv(); + jboolean can_request = env->CallBooleanMethod(helper_, consent_info_helper::GetMethodId(consent_info_helper::kCanRequestAds)); + if (util::HasExceptionOccurred(env)) { + util::CheckAndClearJniExceptions(env); + return false; + } + return can_request ? true : false; +} -void ConsentInfoInternalAndroid::Reset() {} +void ConsentInfoInternalAndroid::Reset() { + JNIEnv* env = GetJNIEnv(); + env->CallVoidMethod(helper_, consent_info_helper::GetMethodId(consent_info_helper::kReset)); + util::CheckAndClearJniExceptions(env); +} JNIEnv* ConsentInfoInternalAndroid::GetJNIEnv() { return firebase::util::GetThreadsafeJNIEnv(java_vm_); } jobject ConsentInfoInternalAndroid::activity() { return activity_; } + void ConsentInfoInternalAndroid::CompleteFutureFromJniCallback(JNIEnv* env, + ConsentInfoFn future_fn, FutureHandleId handle_id, + int java_error_code, const char* error_message) { + if (!futures()->ValidFuture(handle_id)) { + // This future is no longer valid, so no need to complete it. + return; + } + FutureHandle raw_handle(handle_id); + SafeFutureHandle handle(raw_handle); + if (future_fn == kConsentInfoFnRequestConsentInfoUpdate) { + // RequestConsentInfoUpdate uses the ConsentRequestError enum. + ConsentRequestError error_code = + CppConsentRequestErrorFromAndroidFormError(java_error_code, error_message); + if (error_message == nullptr) { + error_message = GetConsentRequestErrorMessage(error_code); + } + CompleteFuture(handle, error_code, error_message); + } else { + // All other methods use the ConsentFormError enum. + ConsentFormError error_code = + CppConsentFormErrorFromAndroidFormError(java_error_code, error_message); + if (error_message == nullptr) { + error_message = GetConsentFormErrorMessage(error_code); + } + CompleteFuture(handle, error_code, error_message); + } +} + } // namespace internal } // namespace ump } // namespace gma diff --git a/gma/src/android/ump/consent_info_internal_android.h b/gma/src/android/ump/consent_info_internal_android.h index 5a234a964c..8ec9f6b5a5 100644 --- a/gma/src/android/ump/consent_info_internal_android.h +++ b/gma/src/android/ump/consent_info_internal_android.h @@ -17,7 +17,10 @@ #ifndef FIREBASE_GMA_SRC_ANDROID_UMP_CONSENT_INFO_INTERNAL_ANDROID_H_ #define FIREBASE_GMA_SRC_ANDROID_UMP_CONSENT_INFO_INTERNAL_ANDROID_H_ +#include + #include "app/src/util_android.h" +#include "firebase/internal/mutex.h" #include "gma/src/common/ump/consent_info_internal.h" namespace firebase { @@ -52,10 +55,29 @@ class ConsentInfoInternalAndroid : public ConsentInfoInternal { JNIEnv* GetJNIEnv(); jobject activity(); + private: + static void JNI_ConsentInfoHelper_completeFuture(JNIEnv* env, + jclass clazz, + jint future_fn, + jlong consent_info_internal_ptr, + jlong future_handle, + jint error_code, + jobject error_message_obj); + + void CompleteFutureFromJniCallback(JNIEnv* env, + ConsentInfoFn future_fn, FutureHandleId handle_id, + int error_code, const char* error_message); + + static ConsentInfoInternalAndroid* s_instance; + static firebase::Mutex s_instance_mutex; + JavaVM* java_vm_; jobject activity_; jobject helper_; + + // Needed for GetConsentFormStatus to return Unknown. + bool has_requested_consent_info_update_; }; } // namespace internal diff --git a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java index 7d53dcd38c..ce1a7260f7 100644 --- a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java @@ -20,9 +20,14 @@ import android.content.Context; import android.util.Log; import com.google.android.ump.ConsentDebugSettings; +import com.google.android.ump.ConsentForm; +import com.google.android.ump.ConsentForm.OnConsentFormDismissedListener; import com.google.android.ump.ConsentInformation; +import com.google.android.ump.ConsentInformation.OnConsentFormLoadFailureListener(); +import com.google.android.ump.ConsentInformation.OnConsentFormLoadSuccessListener(); import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateFailureListener; import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateSuccessListener; +import com.google.android.ump.ConsentInformation.PrivacyOptionsRequirementStatus; import com.google.android.ump.ConsentRequestParameters; import com.google.android.ump.FormError; import com.google.android.ump.UserMessagingPlatform; @@ -36,27 +41,48 @@ public class ConsentInfoHelper { private static final long CPP_NULLPTR = 0; // Synchronization object for thread safe access to: - private final Object mLock; + private final Object mLock = null; + // Pointer to the internal ConsentInfoInternalAndroid C++ object. + // This can be reset back to 0 by calling disconnect(). + private long mInternalPtr = 0; + // The Activity that this was initialized with. + private Activity mActivity = null; + // The loaded consent form, if any. + private ConsentForm mConsentForm = null; - private long mInternalPtr; + // Create our own local passthrough version of these Enum object values + // as integers, to make it easier for the C++ SDK to access them. + public static final int PRIVACY_OPTIONS_REQUIREMENT_UNKNOWN = + PrivacyOptionsRequirementStatus.Unknown.ordinal(); + public static final int PRIVACY_OPTIONS_REQUIREMENT_REQUIRED = + PrivacyOptionsRequirementStatus.Required.ordinal(); + public static final int PRIVACY_OPTIONS_REQUIREMENT_NOT_REQUIRED = + PrivacyOptionsRequirementStatus.NotRequired.ordinal(); - private Activity mActivity; + // Enum values for tracking which function we are calling back. + public static final int FUNCTION_REQUEST_CONSENT_INFO_UPDATE = 0; + public static final int FUNCTION_LOAD_CONSENT_FORM = 1; + public static final int FUNCTION_SHOW_CONSENT_FORM = 2; + public static final int FUNCTION_LOAD_AND_SHOW_CONSENT_FORM_IF_REQUIRED = 3; + public static final int FUNCTION_SHOW_PRIVACY_OPTIONS_FORM = 4; public ConsentInfoHelper(long consentInfoInternalPtr, Activity activity) { mInternalPtr = consentInfoInternalPtr; mLock = new Object(); mActivity = activity; // Test the callbacks and fail quickly if something's wrong. - completeRequestConsentInfoUpdate(CPP_NULLPTR, 0, ""); + completeFuture(CPP_NULLPTR, CPP_NULLPTR, -1, 0, ""); } - int getConsentStatus() { + public int getConsentStatus() { ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); return consentInfo.getConsentStatus(); } - void requestConsentInfoUpdate( - boolean tagForUnderAgeOfConsent, int debugGeography, ArrayList debugIdList) { + public void requestConsentInfoUpdate(final long futureHandle, boolean tagForUnderAgeOfConsent, + int debugGeography, ArrayList debugIdList) { + if (mInternalPtr == null) return; + ConsentDebugSettings.Builder debugSettingsBuilder = null; if (debugGeography != ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_DISABLED) { @@ -89,16 +115,16 @@ public void run() { @Override public void onConsentInfoUpdateSuccess() { synchronized (mLock) { - completeRequestConsentInfoUpdate(mInternalPtr, 0, ""); + completeFuture(mInternalPtr, futureHandle, 0, ""); } } }, new OnConsentInfoUpdateFailureListener() { @Override - public void onConsentInfoUpdateFailure(FormError requestConsentError) { + public void onConsentInfoUpdateFailure(FormError formError) { synchronized (mLock) { - completeRequestConsentInfoUpdate(mInternalPtr, requestConsentError.getErrorCode(), - requestConsentError.getMessage()); + completeFuture( + mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); } } }); @@ -106,25 +132,130 @@ public void onConsentInfoUpdateFailure(FormError requestConsentError) { }); } - /** Disconnect the helper from the native object. */ - public void disconnect() { + public void loadConsentForm(final long futureHandle) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + UserMessagingPlatform.loadConsentForm(mActivity, + new OnConsentFormLoadSuccessListener() { + @Override + public void onConsentFormLoadSuccess(ConsentForm form) { + synchronized (mLock) { + mConsentForm = form; + completeFuture(mInternalPtr, futureHandle, 0, ""); + } + } + }, + new OnConsentFormLoadFailureListener() { + @Override + public void onConsentFormLoadFailure(FormError formError) { + synchronized (mLock) { + mConsentForm = null; + completeFuture( + mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + } + } + }); + } + }); + } + + public boolean showConsentForm(final long futureHandle, final Activity activity) { + ConsentForm consentForm; synchronized (mLock) { - mInternalPtr = CPP_NULLPTR; + if (mConsentForm == null) { + // Consent form was not loaded, return an error. + return false; + } + consentForm = mConsentForm; + mConsentForm = null; } + final ConsentForm consentFormForThread = consentForm; + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + consentFormForThread.show(activity, new OnConsentFormDismissedListener() { + @Override + public void onConsentFormDismissed(FormError formError) { + synchronized (mLock) { + if (formError == null) { + completeFuture(mInternalPtr, futureHandle, 0, ""); + } else { + completeFuture( + mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + } + } + } + }); + } + }); + // Consent form is loaded. + return true; } - public static native void completeRequestConsentInfoUpdate( - long nativeInternalPtr, int errorCode, String errorMessage); + public void loadAndShowConsentFormIfRequired(final long futureHandle, final Activity activity) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run(){ + UserMessagingPlatform.loadAndShowConsentFormIfRequired(activity){ + new OnConsentFormDismissedListener(){ + @Override + public void onConsentFormDismissed(FormError formError) { + synchronized (mLock){ + if (formError == null){completeFuture(mInternalPtr, futureHandle, 0, ""); + } else { + completeFuture(mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + } + } + } + }); + } + }); + } - public static native void completeLoadConsentForm( - long nativeInternalPtr, int errorCode, String errorMessage); + public int getPrivacyOptionsRequirementStatus { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + return consentInfo.getPrivacyOptionsRequirementStatus().ordinal(); + } - public static native void completeShowConsentForm( - long nativeInternalPtr, int errorCode, String errorMessage); + public void showPrivacyOptionsForm(final long futureHandle, final Activity activity) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run(){ + UserMessagingPlatform.showPrivacyOptionsForm(activity){new OnConsentFormDismissedListener(){ + @Override public void onConsentFormDismissed(FormError formError){synchronized (mLock){ + if (formError == null){completeFuture(mInternalPtr, futureHandle, 0, ""); + } else { + completeFuture(mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + } + } + } + }); + } + }); + } - public static native void completeLoadAndShowIfRequired( - long nativeInternalPtr, int errorCode, String errorMessage); + public boolean canRequestAds() { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + return consentInfo.canRequestAds(); + } + + public boolean isConsentFormAvailable() { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + return consentInfo.isConsentFormAvailable(); + } + + public void reset() { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + consentInfo.reset(); + } + + /** Disconnect the helper from the native object. */ + public void disconnect() { + synchronized (mLock) { + mInternalPtr = CPP_NULLPTR; + } + } - public static native void completeShowPrivacyOptionsForm( - long nativeInternalPtr, int errorCode, String errorMessage); + public static native void completeFuture(int futureFn, long nativeInternalPtr, long futureHandle, int errorCode, String errorMessage); } From a61afb8d7e124be57b631cd5e03e720ac46412f3 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 13:30:24 -0700 Subject: [PATCH 20/56] Format code. --- app/src/util_android.h | 3 +- .../ump/consent_info_internal_android.cc | 149 +++++++++++------- .../ump/consent_info_internal_android.h | 20 +-- .../ump/internal/cpp/ConsentInfoHelper.java | 126 ++++++++------- 4 files changed, 166 insertions(+), 132 deletions(-) diff --git a/app/src/util_android.h b/app/src/util_android.h index 4102ea872c..c5e82ca847 100644 --- a/app/src/util_android.h +++ b/app/src/util_android.h @@ -1097,7 +1097,8 @@ bool CheckAndClearJniExceptions(JNIEnv* env); std::string GetAndClearExceptionMessage(JNIEnv* env); // Returns true if an JNI exception occurred, false otherwise. -// Does not clear the exception, so you can then call GetAndClearExceptionMessage(). +// Does not clear the exception, so you can then call +// GetAndClearExceptionMessage(). bool HasExceptionOccurred(JNIEnv* env); // Returns the message from an exception. diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 6070fb8f20..b0b4230d85 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -183,38 +183,44 @@ static struct { jint privacy_options_requirement_not_required; } g_enum_cache; -void ConsentInfoInternalAndroid::JNI_ConsentInfoHelper_completeFuture(JNIEnv* env, - jclass clazz, - jint future_fn, - jlong consent_info_internal_ptr, - jlong future_handle, - jint error_code, - jobject error_message_obj) { - if (consent_info_internal_ptr == 0 || future_fn < 0 || future_fn >= kConsentInfoFnCount) { - // Calling this with a null pointer or invalid fn is a no-op, so just return. +void ConsentInfoInternalAndroid::JNI_ConsentInfoHelper_completeFuture( + JNIEnv* env, jclass clazz, jint future_fn, jlong consent_info_internal_ptr, + jlong future_handle, jint error_code, jobject error_message_obj) { + if (consent_info_internal_ptr == 0 || future_fn < 0 || + future_fn >= kConsentInfoFnCount) { + // Calling this with a null pointer or invalid fn is a no-op, so just + // return. return; } { MutexLock lock(s_instance_mutex); - std::string error_message = error_message_obj ? util::JniStringToString(env, error_message_obj) : ""; - ConsentInfoInternalAndroid* instance = reinterpret_cast(consent_info_internal_ptr); + std::string error_message = + error_message_obj ? util::JniStringToString(env, error_message_obj) + : ""; + ConsentInfoInternalAndroid* instance = + reinterpret_cast( + consent_info_internal_ptr); if (s_instance != instance) { - // If the instance we were called with does not match the current instance, a bad race condition - // has occurred (whereby while waiting for the operation to complete, ConsentInfo was deleted and then recreated). + // If the instance we were called with does not match the current + // instance, a bad race condition has occurred (whereby while waiting for + // the operation to complete, ConsentInfo was deleted and then recreated). // In that case, fully ignore this callback. return; } - instance->CompleteFutureFromJniCallback(env, - static_cast(future_fn), - static_cast(future_handle), - static_cast(error_code), - error_message.length() > 0 ? error_message.c_str() : nullptr); + instance->CompleteFutureFromJniCallback( + env, static_cast(future_fn), + static_cast(future_handle), + static_cast(error_code), + error_message.length() > 0 ? error_message.c_str() : nullptr); } } ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, jobject activity) - : java_vm_(nullptr), activity_(nullptr), helper_(nullptr), has_requested_consent_info_update_(false) { + : java_vm_(nullptr), + activity_(nullptr), + helper_(nullptr), + has_requested_consent_info_update_(false) { MutexLock lock(s_instance_mutex); FIREBASE_ASSERT(s_instance == nullptr); @@ -240,10 +246,10 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, } static const JNINativeMethod kConsentInfoHelperNativeMethods[] = { {"completeFuture", "(IJJILjava/lang/String;)V", - reinterpret_cast(&JNI_ConsentInfoHelper_completeFuture)} - }; - if (!consent_info_helper::RegisterNatives(env, kConsentInfoHelperNativeMethods, - FIREBASE_ARRAYSIZE(kConsentInfoHelperNativeMethods))) { + reinterpret_cast(&JNI_ConsentInfoHelper_completeFuture)}}; + if (!consent_info_helper::RegisterNatives( + env, kConsentInfoHelperNativeMethods, + FIREBASE_ARRAYSIZE(kConsentInfoHelperNativeMethods))) { util::CheckAndClearJniExceptions(env); ReleaseClasses(env); util::Terminate(env); @@ -329,7 +335,8 @@ CppPrivacyOptionsRequirementStatusFromAndroid(jint status) { return kPrivacyOptionsRequirementStatusUnknown; } -static jint AndroidDebugGeographyFromCppDebugGeography(ConsentDebugGeography geo) { +static jint AndroidDebugGeographyFromCppDebugGeography( + ConsentDebugGeography geo) { // Cache values the first time this function runs. switch (geo) { case kConsentDebugGeographyDisabled: @@ -343,7 +350,8 @@ static jint AndroidDebugGeographyFromCppDebugGeography(ConsentDebugGeography geo } } -static ConsentRequestError CppConsentRequestErrorFromAndroidFormError(jint error, const char* message = nullptr) { +static ConsentRequestError CppConsentRequestErrorFromAndroidFormError( + jint error, const char* message = nullptr) { // Cache values the first time this function runs. if (error == g_enum_cache.formerror_success) return kConsentRequestSuccess; if (error == g_enum_cache.formerror_internal) @@ -355,7 +363,8 @@ static ConsentRequestError CppConsentRequestErrorFromAndroidFormError(jint error return kConsentRequestErrorUnknown; } -static ConsentFormError CppConsentFormErrorFromAndroidFormError(jint error, const char* message = nullptr) { +static ConsentFormError CppConsentFormErrorFromAndroidFormError( + jint error, const char* message = nullptr) { if (error == g_enum_cache.formerror_success) return kConsentFormSuccess; if (error == g_enum_cache.formerror_internal) return kConsentFormErrorInternal; @@ -378,17 +387,20 @@ Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( jlong future_handle = static_cast(handle.get().id()); jboolean tag_for_under_age_of_consent = static_cast(params.tag_for_under_age_of_consent); - jint debug_geography = AndroidDebugGeographyFromCppDebugGeography(params.debug_settings.debug_geography); + jint debug_geography = AndroidDebugGeographyFromCppDebugGeography( + params.debug_settings.debug_geography); jobject debug_device_ids_list = - util::StdVectorToJavaList(env, params.debug_settings.debug_device_ids); + util::StdVectorToJavaList(env, params.debug_settings.debug_device_ids); env->CallVoidMethod(helper_, - consent_info_helper::GetMethodId(consent_info_helper::kRequestConsentInfoUpdate), - future_handle, tag_for_under_age_of_consent, debug_geography, - debug_device_ids_list); - + consent_info_helper::GetMethodId( + consent_info_helper::kRequestConsentInfoUpdate), + future_handle, tag_for_under_age_of_consent, + debug_geography, debug_device_ids_list); + if (util::HasExceptionOccurred(env)) { std::string exception_message = util::GetAndClearExceptionMessage(env); - CompleteFuture(handle, kConsentRequestErrorInternal, exception_message.c_str()); + CompleteFuture(handle, kConsentRequestErrorInternal, + exception_message.c_str()); } else { has_requested_consent_info_update_ = true; } @@ -418,13 +430,14 @@ ConsentFormStatus ConsentInfoInternalAndroid::GetConsentFormStatus() { } JNIEnv* env = GetJNIEnv(); jboolean is_available = env->CallBooleanMethod( - helper_, - consent_info_helper::GetMethodId(consent_info_helper::kIsConsentFormAvailable)); + helper_, consent_info_helper::GetMethodId( + consent_info_helper::kIsConsentFormAvailable)); if (util::HasExceptionOccurred(env)) { util::CheckAndClearJniExceptions(env); return kConsentFormStatusUnknown; } - return is_available ? kConsentFormStatusAvailable : kConsentFormStatusUnavailable; + return is_available ? kConsentFormStatusAvailable + : kConsentFormStatusUnavailable; } Future ConsentInfoInternalAndroid::LoadConsentForm() { @@ -432,12 +445,14 @@ Future ConsentInfoInternalAndroid::LoadConsentForm() { JNIEnv* env = GetJNIEnv(); jlong future_handle = static_cast(handle.get().id()); - env->CallVoidMethod(helper_, - consent_info_helper::GetMethodId(consent_info_helper::kLoadConsentForm), - future_handle); + env->CallVoidMethod( + helper_, + consent_info_helper::GetMethodId(consent_info_helper::kLoadConsentForm), + future_handle); if (util::HasExceptionOccurred(env)) { std::string exception_message = util::GetAndClearExceptionMessage(env); - CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + CompleteFuture(handle, kConsentFormErrorInternal, + exception_message.c_str()); } return MakeFuture(futures(), handle); } @@ -447,15 +462,19 @@ Future ConsentInfoInternalAndroid::ShowConsentForm(FormParent parent) { JNIEnv* env = GetJNIEnv(); jlong future_handle = static_cast(handle.get().id()); - jboolean success = env->CallBooleanMethod(helper_, - consent_info_helper::GetMethodId(consent_info_helper::kShowConsentForm), - future_handle, parent); + jboolean success = env->CallBooleanMethod( + helper_, + consent_info_helper::GetMethodId(consent_info_helper::kShowConsentForm), + future_handle, parent); if (util::HasExceptionOccurred(env)) { std::string exception_message = util::GetAndClearExceptionMessage(env); - CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + CompleteFuture(handle, kConsentFormErrorInternal, + exception_message.c_str()); } else if (!success) { - CompleteFuture(handle, kConsentFormErrorUnavailable, - "The consent form is unavailable. Please call LoadConsentForm and ensure it completes successfully before calling ShowConsentForm."); + CompleteFuture( + handle, kConsentFormErrorUnavailable, + "The consent form is unavailable. Please call LoadConsentForm and " + "ensure it completes successfully before calling ShowConsentForm."); } return MakeFuture(futures(), handle); } @@ -468,12 +487,15 @@ Future ConsentInfoInternalAndroid::LoadAndShowConsentFormIfRequired( JNIEnv* env = GetJNIEnv(); jlong future_handle = static_cast(handle.get().id()); - env->CallVoidMethod(helper_, - consent_info_helper::GetMethodId(consent_info_helper::kLoadAndShowConsentFormIfRequired), - future_handle, parent); + env->CallVoidMethod( + helper_, + consent_info_helper::GetMethodId( + consent_info_helper::kLoadAndShowConsentFormIfRequired), + future_handle, parent); if (util::HasExceptionOccurred(env)) { std::string exception_message = util::GetAndClearExceptionMessage(env); - CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + CompleteFuture(handle, kConsentFormErrorInternal, + exception_message.c_str()); } return MakeFuture(futures(), handle); @@ -500,11 +522,13 @@ Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( jlong future_handle = static_cast(handle.get().id()); env->CallVoidMethod(helper_, - consent_info_helper::GetMethodId(consent_info_helper::kShowPrivacyOptionsForm), - future_handle, parent); + consent_info_helper::GetMethodId( + consent_info_helper::kShowPrivacyOptionsForm), + future_handle, parent); if (util::HasExceptionOccurred(env)) { std::string exception_message = util::GetAndClearExceptionMessage(env); - CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); + CompleteFuture(handle, kConsentFormErrorInternal, + exception_message.c_str()); } return MakeFuture(futures(), handle); @@ -512,7 +536,9 @@ Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( bool ConsentInfoInternalAndroid::CanRequestAds() { JNIEnv* env = GetJNIEnv(); - jboolean can_request = env->CallBooleanMethod(helper_, consent_info_helper::GetMethodId(consent_info_helper::kCanRequestAds)); + jboolean can_request = env->CallBooleanMethod( + helper_, + consent_info_helper::GetMethodId(consent_info_helper::kCanRequestAds)); if (util::HasExceptionOccurred(env)) { util::CheckAndClearJniExceptions(env); return false; @@ -522,7 +548,8 @@ bool ConsentInfoInternalAndroid::CanRequestAds() { void ConsentInfoInternalAndroid::Reset() { JNIEnv* env = GetJNIEnv(); - env->CallVoidMethod(helper_, consent_info_helper::GetMethodId(consent_info_helper::kReset)); + env->CallVoidMethod( + helper_, consent_info_helper::GetMethodId(consent_info_helper::kReset)); util::CheckAndClearJniExceptions(env); } @@ -531,9 +558,9 @@ JNIEnv* ConsentInfoInternalAndroid::GetJNIEnv() { } jobject ConsentInfoInternalAndroid::activity() { return activity_; } - void ConsentInfoInternalAndroid::CompleteFutureFromJniCallback(JNIEnv* env, - ConsentInfoFn future_fn, FutureHandleId handle_id, - int java_error_code, const char* error_message) { +void ConsentInfoInternalAndroid::CompleteFutureFromJniCallback( + JNIEnv* env, ConsentInfoFn future_fn, FutureHandleId handle_id, + int java_error_code, const char* error_message) { if (!futures()->ValidFuture(handle_id)) { // This future is no longer valid, so no need to complete it. return; @@ -542,8 +569,8 @@ jobject ConsentInfoInternalAndroid::activity() { return activity_; } SafeFutureHandle handle(raw_handle); if (future_fn == kConsentInfoFnRequestConsentInfoUpdate) { // RequestConsentInfoUpdate uses the ConsentRequestError enum. - ConsentRequestError error_code = - CppConsentRequestErrorFromAndroidFormError(java_error_code, error_message); + ConsentRequestError error_code = CppConsentRequestErrorFromAndroidFormError( + java_error_code, error_message); if (error_message == nullptr) { error_message = GetConsentRequestErrorMessage(error_code); } @@ -551,7 +578,7 @@ jobject ConsentInfoInternalAndroid::activity() { return activity_; } } else { // All other methods use the ConsentFormError enum. ConsentFormError error_code = - CppConsentFormErrorFromAndroidFormError(java_error_code, error_message); + CppConsentFormErrorFromAndroidFormError(java_error_code, error_message); if (error_message == nullptr) { error_message = GetConsentFormErrorMessage(error_code); } diff --git a/gma/src/android/ump/consent_info_internal_android.h b/gma/src/android/ump/consent_info_internal_android.h index 8ec9f6b5a5..faf474191c 100644 --- a/gma/src/android/ump/consent_info_internal_android.h +++ b/gma/src/android/ump/consent_info_internal_android.h @@ -55,19 +55,15 @@ class ConsentInfoInternalAndroid : public ConsentInfoInternal { JNIEnv* GetJNIEnv(); jobject activity(); - private: - static void JNI_ConsentInfoHelper_completeFuture(JNIEnv* env, - jclass clazz, - jint future_fn, - jlong consent_info_internal_ptr, - jlong future_handle, - jint error_code, - jobject error_message_obj); - - void CompleteFutureFromJniCallback(JNIEnv* env, - ConsentInfoFn future_fn, FutureHandleId handle_id, - int error_code, const char* error_message); + static void JNI_ConsentInfoHelper_completeFuture( + JNIEnv* env, jclass clazz, jint future_fn, + jlong consent_info_internal_ptr, jlong future_handle, jint error_code, + jobject error_message_obj); + + void CompleteFutureFromJniCallback(JNIEnv* env, ConsentInfoFn future_fn, + FutureHandleId handle_id, int error_code, + const char* error_message); static ConsentInfoInternalAndroid* s_instance; static firebase::Mutex s_instance_mutex; diff --git a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java index ce1a7260f7..c6654a179b 100644 --- a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java @@ -81,8 +81,9 @@ public int getConsentStatus() { public void requestConsentInfoUpdate(final long futureHandle, boolean tagForUnderAgeOfConsent, int debugGeography, ArrayList debugIdList) { - if (mInternalPtr == null) return; - + if (mInternalPtr == null) + return; + ConsentDebugSettings.Builder debugSettingsBuilder = null; if (debugGeography != ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_DISABLED) { @@ -193,69 +194,78 @@ public void onConsentFormDismissed(FormError formError) { return true; } - public void loadAndShowConsentFormIfRequired(final long futureHandle, final Activity activity) { - mActivity.runOnUiThread(new Runnable() { - @Override - public void run(){ - UserMessagingPlatform.loadAndShowConsentFormIfRequired(activity){ - new OnConsentFormDismissedListener(){ - @Override - public void onConsentFormDismissed(FormError formError) { - synchronized (mLock){ - if (formError == null){completeFuture(mInternalPtr, futureHandle, 0, ""); - } else { - completeFuture(mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); - } - } - } - }); + public void loadAndShowConsentFormIfRequired(final long futureHandle, final Activity activity) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + UserMessagingPlatform.loadAndShowConsentFormIfRequired(activity) { + new OnConsentFormDismissedListener() { + @Override + public void onConsentFormDismissed(FormError formError) { + synchronized (mLock) { + if (formError == null) { + completeFuture(mInternalPtr, futureHandle, 0, ""); + } else { + completeFuture( + mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); } - }); - } + } + } + } + } + } + }); + } - public int getPrivacyOptionsRequirementStatus { - ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); - return consentInfo.getPrivacyOptionsRequirementStatus().ordinal(); - } + public int getPrivacyOptionsRequirementStatus { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + return consentInfo.getPrivacyOptionsRequirementStatus().ordinal(); + } - public void showPrivacyOptionsForm(final long futureHandle, final Activity activity) { - mActivity.runOnUiThread(new Runnable() { - @Override - public void run(){ - UserMessagingPlatform.showPrivacyOptionsForm(activity){new OnConsentFormDismissedListener(){ - @Override public void onConsentFormDismissed(FormError formError){synchronized (mLock){ - if (formError == null){completeFuture(mInternalPtr, futureHandle, 0, ""); - } else { - completeFuture(mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); - } - } - } - }); + public void showPrivacyOptionsForm(final long futureHandle, final Activity activity) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + UserMessagingPlatform.showPrivacyOptionsForm(activity) { + new OnConsentFormDismissedListener() { + @Override + public void onConsentFormDismissed(FormError formError) { + synchronized (mLock) { + if (formError == null) { + completeFuture(mInternalPtr, futureHandle, 0, ""); + } else { + completeFuture( + mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); } - }); - } + } + } + } + } + } + }); + } - public boolean canRequestAds() { - ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); - return consentInfo.canRequestAds(); - } + public boolean canRequestAds() { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + return consentInfo.canRequestAds(); + } - public boolean isConsentFormAvailable() { - ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); - return consentInfo.isConsentFormAvailable(); - } + public boolean isConsentFormAvailable() { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + return consentInfo.isConsentFormAvailable(); + } - public void reset() { - ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); - consentInfo.reset(); - } + public void reset() { + ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); + consentInfo.reset(); + } - /** Disconnect the helper from the native object. */ - public void disconnect() { - synchronized (mLock) { - mInternalPtr = CPP_NULLPTR; - } + /** Disconnect the helper from the native object. */ + public void disconnect() { + synchronized (mLock) { + mInternalPtr = CPP_NULLPTR; } - - public static native void completeFuture(int futureFn, long nativeInternalPtr, long futureHandle, int errorCode, String errorMessage); + } + public static native void completeFuture( + int futureFn, long nativeInternalPtr, long futureHandle, int errorCode, String errorMessage); } From bc1aa9caf102fc2a8d96bf444da8ea6090b9cb59 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 13:31:55 -0700 Subject: [PATCH 21/56] Fix JNI type. --- gma/src/android/ump/consent_info_internal_android.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index b0b4230d85..0023ac1c57 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -39,15 +39,15 @@ firebase::Mutex ConsentInfoInternalAndroid::s_instance_mutex; X(Constructor, "", "(JLandroid/app/Activity;)V"), \ X(GetConsentStatus, "getConsentStatus", "()I"), \ X(RequestConsentInfoUpdate, "requestConsentInfoUpdate", \ - "(ZILjava/util/ArrayList;)V"), \ - X(LoadConsentForm, "loadConsentForm", "(Z)V"), \ - X(ShowConsentForm, "showConsentForm", "(ZLandroid/app/Activity;)Z"), \ + "(JZILjava/util/ArrayList;)V"), \ + X(LoadConsentForm, "loadConsentForm", "(J)V"), \ + X(ShowConsentForm, "showConsentForm", "(JLandroid/app/Activity;)Z"), \ X(LoadAndShowConsentFormIfRequired, "loadAndShowConsentFormIfRequired", \ - "(ZLandroid/app/Activity;)V"), \ + "(JLandroid/app/Activity;)V"), \ X(GetPrivacyOptionsRequirementStatus, "getPrivacyOptionsRequirementStatus", \ "()I"), \ X(ShowPrivacyOptionsForm, "showPrivacyOptionsForm", \ - "(ZLandroid/app/Activity;)V"), \ + "(JLandroid/app/Activity;)V"), \ X(Reset, "reset", "()V"), \ X(CanRequestAds, "canRequestAds", "()Z"), \ X(IsConsentFormAvailable, "isConsentFormAvailable", "()Z"), \ From 5beea2e56d9fd5f954c0cecce3c918c5c02db3d6 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 14:48:10 -0700 Subject: [PATCH 22/56] Fix Java build errors and format code. --- .../ump/consent_info_internal_android.cc | 51 ++++----- .../ump/internal/cpp/ConsentInfoHelper.java | 100 ++++++++++-------- 2 files changed, 78 insertions(+), 73 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 0023ac1c57..1486391b65 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -186,33 +186,28 @@ static struct { void ConsentInfoInternalAndroid::JNI_ConsentInfoHelper_completeFuture( JNIEnv* env, jclass clazz, jint future_fn, jlong consent_info_internal_ptr, jlong future_handle, jint error_code, jobject error_message_obj) { - if (consent_info_internal_ptr == 0 || future_fn < 0 || + MutexLock lock(s_instance_mutex); + if (consent_info_internal_ptr == 0 || s_instance == 0 || future_fn < 0 || future_fn >= kConsentInfoFnCount) { - // Calling this with a null pointer or invalid fn is a no-op, so just - // return. + // Calling this with a null pointer or invalid fn, or if there is no active + // instance, is a no-op, so just return. return; } - { - MutexLock lock(s_instance_mutex); - std::string error_message = - error_message_obj ? util::JniStringToString(env, error_message_obj) - : ""; - ConsentInfoInternalAndroid* instance = - reinterpret_cast( - consent_info_internal_ptr); - if (s_instance != instance) { - // If the instance we were called with does not match the current - // instance, a bad race condition has occurred (whereby while waiting for - // the operation to complete, ConsentInfo was deleted and then recreated). - // In that case, fully ignore this callback. - return; - } - instance->CompleteFutureFromJniCallback( - env, static_cast(future_fn), - static_cast(future_handle), - static_cast(error_code), - error_message.length() > 0 ? error_message.c_str() : nullptr); + ConsentInfoInternalAndroid* instance = + reinterpret_cast(consent_info_internal_ptr); + if (s_instance != instance) { + // If the instance we were called with does not match the current + // instance, a bad race condition has occurred (whereby while waiting for + // the operation to complete, ConsentInfo was deleted and then recreated). + // In that case, fully ignore this callback. + return; } + std::string error_message = + error_message_obj ? util::JniStringToString(env, error_message_obj) : ""; + instance->CompleteFutureFromJniCallback( + env, static_cast(future_fn), + static_cast(future_handle), static_cast(error_code), + error_message.length() > 0 ? error_message.c_str() : nullptr); } ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, @@ -386,7 +381,7 @@ Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( jlong future_handle = static_cast(handle.get().id()); jboolean tag_for_under_age_of_consent = - static_cast(params.tag_for_under_age_of_consent); + params.tag_for_under_age_of_consent ? JNI_TRUE : JNI_FALSE; jint debug_geography = AndroidDebugGeographyFromCppDebugGeography( params.debug_settings.debug_geography); jobject debug_device_ids_list = @@ -436,8 +431,8 @@ ConsentFormStatus ConsentInfoInternalAndroid::GetConsentFormStatus() { util::CheckAndClearJniExceptions(env); return kConsentFormStatusUnknown; } - return is_available ? kConsentFormStatusAvailable - : kConsentFormStatusUnavailable; + return (is_available == JNI_FALSE) ? kConsentFormStatusUnavailable + : kConsentFormStatusAvailable; } Future ConsentInfoInternalAndroid::LoadConsentForm() { @@ -470,7 +465,7 @@ Future ConsentInfoInternalAndroid::ShowConsentForm(FormParent parent) { std::string exception_message = util::GetAndClearExceptionMessage(env); CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); - } else if (!success) { + } else if (success == JNI_FALSE) { CompleteFuture( handle, kConsentFormErrorUnavailable, "The consent form is unavailable. Please call LoadConsentForm and " @@ -543,7 +538,7 @@ bool ConsentInfoInternalAndroid::CanRequestAds() { util::CheckAndClearJniExceptions(env); return false; } - return can_request ? true : false; + return (can_request == JNI_FALSE) ? false : true; } void ConsentInfoInternalAndroid::Reset() { diff --git a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java index c6654a179b..f52ab25f41 100644 --- a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java @@ -23,14 +23,14 @@ import com.google.android.ump.ConsentForm; import com.google.android.ump.ConsentForm.OnConsentFormDismissedListener; import com.google.android.ump.ConsentInformation; -import com.google.android.ump.ConsentInformation.OnConsentFormLoadFailureListener(); -import com.google.android.ump.ConsentInformation.OnConsentFormLoadSuccessListener(); import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateFailureListener; import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateSuccessListener; import com.google.android.ump.ConsentInformation.PrivacyOptionsRequirementStatus; import com.google.android.ump.ConsentRequestParameters; import com.google.android.ump.FormError; import com.google.android.ump.UserMessagingPlatform; +import com.google.android.ump.UserMessagingPlatform.OnConsentFormLoadFailureListener; +import com.google.android.ump.UserMessagingPlatform.OnConsentFormLoadSuccessListener; import java.util.ArrayList; /** @@ -41,7 +41,7 @@ public class ConsentInfoHelper { private static final long CPP_NULLPTR = 0; // Synchronization object for thread safe access to: - private final Object mLock = null; + private final Object mLock = new Object(); // Pointer to the internal ConsentInfoInternalAndroid C++ object. // This can be reset back to 0 by calling disconnect(). private long mInternalPtr = 0; @@ -53,11 +53,11 @@ public class ConsentInfoHelper { // Create our own local passthrough version of these Enum object values // as integers, to make it easier for the C++ SDK to access them. public static final int PRIVACY_OPTIONS_REQUIREMENT_UNKNOWN = - PrivacyOptionsRequirementStatus.Unknown.ordinal(); + PrivacyOptionsRequirementStatus.UNKNOWN.ordinal(); public static final int PRIVACY_OPTIONS_REQUIREMENT_REQUIRED = - PrivacyOptionsRequirementStatus.Required.ordinal(); + PrivacyOptionsRequirementStatus.REQUIRED.ordinal(); public static final int PRIVACY_OPTIONS_REQUIREMENT_NOT_REQUIRED = - PrivacyOptionsRequirementStatus.NotRequired.ordinal(); + PrivacyOptionsRequirementStatus.NOT_REQUIRED.ordinal(); // Enum values for tracking which function we are calling back. public static final int FUNCTION_REQUEST_CONSENT_INFO_UPDATE = 0; @@ -68,10 +68,9 @@ public class ConsentInfoHelper { public ConsentInfoHelper(long consentInfoInternalPtr, Activity activity) { mInternalPtr = consentInfoInternalPtr; - mLock = new Object(); mActivity = activity; // Test the callbacks and fail quickly if something's wrong. - completeFuture(CPP_NULLPTR, CPP_NULLPTR, -1, 0, ""); + completeFuture(-1, CPP_NULLPTR, CPP_NULLPTR, 0, null); } public int getConsentStatus() { @@ -81,8 +80,9 @@ public int getConsentStatus() { public void requestConsentInfoUpdate(final long futureHandle, boolean tagForUnderAgeOfConsent, int debugGeography, ArrayList debugIdList) { - if (mInternalPtr == null) + if (mInternalPtr == 0) return; + final int functionId = FUNCTION_REQUEST_CONSENT_INFO_UPDATE; ConsentDebugSettings.Builder debugSettingsBuilder = null; @@ -116,7 +116,7 @@ public void run() { @Override public void onConsentInfoUpdateSuccess() { synchronized (mLock) { - completeFuture(mInternalPtr, futureHandle, 0, ""); + completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } } }, @@ -124,8 +124,8 @@ public void onConsentInfoUpdateSuccess() { @Override public void onConsentInfoUpdateFailure(FormError formError) { synchronized (mLock) { - completeFuture( - mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + completeFuture(functionId, mInternalPtr, futureHandle, formError.getErrorCode(), + formError.getMessage()); } } }); @@ -134,6 +134,9 @@ public void onConsentInfoUpdateFailure(FormError formError) { } public void loadConsentForm(final long futureHandle) { + if (mInternalPtr == 0) + return; + final int functionId = FUNCTION_LOAD_CONSENT_FORM; mActivity.runOnUiThread(new Runnable() { @Override public void run() { @@ -143,7 +146,7 @@ public void run() { public void onConsentFormLoadSuccess(ConsentForm form) { synchronized (mLock) { mConsentForm = form; - completeFuture(mInternalPtr, futureHandle, 0, ""); + completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } } }, @@ -152,8 +155,8 @@ public void onConsentFormLoadSuccess(ConsentForm form) { public void onConsentFormLoadFailure(FormError formError) { synchronized (mLock) { mConsentForm = null; - completeFuture( - mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + completeFuture(functionId, mInternalPtr, futureHandle, formError.getErrorCode(), + formError.getMessage()); } } }); @@ -162,6 +165,9 @@ public void onConsentFormLoadFailure(FormError formError) { } public boolean showConsentForm(final long futureHandle, final Activity activity) { + if (mInternalPtr == 0) + return false; + final int functionId = FUNCTION_SHOW_CONSENT_FORM; ConsentForm consentForm; synchronized (mLock) { if (mConsentForm == null) { @@ -180,10 +186,10 @@ public void run() { public void onConsentFormDismissed(FormError formError) { synchronized (mLock) { if (formError == null) { - completeFuture(mInternalPtr, futureHandle, 0, ""); + completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } else { - completeFuture( - mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + completeFuture(functionId, mInternalPtr, futureHandle, formError.getErrorCode(), + formError.getMessage()); } } } @@ -195,52 +201,56 @@ public void onConsentFormDismissed(FormError formError) { } public void loadAndShowConsentFormIfRequired(final long futureHandle, final Activity activity) { + if (mInternalPtr == 0) + return; + final int functionId = FUNCTION_LOAD_AND_SHOW_CONSENT_FORM_IF_REQUIRED; mActivity.runOnUiThread(new Runnable() { @Override public void run() { - UserMessagingPlatform.loadAndShowConsentFormIfRequired(activity) { - new OnConsentFormDismissedListener() { - @Override - public void onConsentFormDismissed(FormError formError) { - synchronized (mLock) { - if (formError == null) { - completeFuture(mInternalPtr, futureHandle, 0, ""); - } else { - completeFuture( - mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + UserMessagingPlatform.loadAndShowConsentFormIfRequired( + activity, new OnConsentFormDismissedListener() { + @Override + public void onConsentFormDismissed(FormError formError) { + synchronized (mLock) { + if (formError == null) { + completeFuture(functionId, mInternalPtr, futureHandle, 0, null); + } else { + completeFuture(functionId, mInternalPtr, futureHandle, formError.getErrorCode(), + formError.getMessage()); + } } } - } - } - } + }); } }); } - public int getPrivacyOptionsRequirementStatus { + public int getPrivacyOptionsRequirementStatus() { ConsentInformation consentInfo = UserMessagingPlatform.getConsentInformation(mActivity); return consentInfo.getPrivacyOptionsRequirementStatus().ordinal(); } public void showPrivacyOptionsForm(final long futureHandle, final Activity activity) { + if (mInternalPtr == 0) + return; + final int functionId = FUNCTION_SHOW_PRIVACY_OPTIONS_FORM; mActivity.runOnUiThread(new Runnable() { @Override public void run() { - UserMessagingPlatform.showPrivacyOptionsForm(activity) { - new OnConsentFormDismissedListener() { - @Override - public void onConsentFormDismissed(FormError formError) { - synchronized (mLock) { - if (formError == null) { - completeFuture(mInternalPtr, futureHandle, 0, ""); - } else { - completeFuture( - mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); + UserMessagingPlatform.showPrivacyOptionsForm( + activity, new OnConsentFormDismissedListener() { + @Override + public void onConsentFormDismissed(FormError formError) { + synchronized (mLock) { + if (formError == null) { + completeFuture(functionId, mInternalPtr, futureHandle, 0, null); + } else { + completeFuture(functionId, mInternalPtr, futureHandle, formError.getErrorCode(), + formError.getMessage()); + } } } - } - } - } + }); } }); } From 5262bd4b5a989efba350fe1c049746d00d313e3c Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 15:00:09 -0700 Subject: [PATCH 23/56] Fix call. --- gma/src/android/ump/consent_info_internal_android.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 1486391b65..6db7473048 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -141,6 +141,7 @@ static void ReleaseClasses(JNIEnv* env) { ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { MutexLock lock(s_instance_mutex); + s_instance = nullptr; JNIEnv* env = GetJNIEnv(); env->CallVoidMethod(helper_, consent_info_helper::GetMethodId( @@ -218,6 +219,7 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, has_requested_consent_info_update_(false) { MutexLock lock(s_instance_mutex); FIREBASE_ASSERT(s_instance == nullptr); + s_instance = this; util::Initialize(env, activity); env->GetJavaVM(&java_vm_); @@ -254,7 +256,8 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, jobject helper_ref = env->NewObject( consent_info_helper::GetClass(), consent_info_helper::GetMethodId(consent_info_helper::kConstructor), - reinterpret_cast(this)); + reinterpret_cast(this), + activity); util::CheckAndClearJniExceptions(env); if (!helper_ref) { ReleaseClasses(env); From af4b0243ccc3ab41b1f843c360ecfdcddbdfb79a Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 15:13:19 -0700 Subject: [PATCH 24/56] Fix initialization. --- gma/src/common/ump/consent_info.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gma/src/common/ump/consent_info.cc b/gma/src/common/ump/consent_info.cc index 49b3d39f10..334a11d735 100644 --- a/gma/src/common/ump/consent_info.cc +++ b/gma/src/common/ump/consent_info.cc @@ -60,8 +60,8 @@ ConsentInfo* ConsentInfo::GetInstance(::firebase::InitResult* init_result_out) { #else InitResult result = consent_info->Initialize(); #endif + if (init_result_out) *init_result_out = result; if (result != kInitResultSuccess) { - if (init_result_out) *init_result_out = result; delete consent_info; return nullptr; } From a6a37afb992de1a456e82376c2d1f1eff0877138 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 15:26:15 -0700 Subject: [PATCH 25/56] Fix for debugDeviceId --- .../src/android/android_firebase_test_framework.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/testing/test_framework/src/android/android_firebase_test_framework.cc b/testing/test_framework/src/android/android_firebase_test_framework.cc index 7725db2c9f..e675f4c4ba 100644 --- a/testing/test_framework/src/android/android_firebase_test_framework.cc +++ b/testing/test_framework/src/android/android_firebase_test_framework.cc @@ -275,14 +275,21 @@ std::string FirebaseTest::GetDebugDeviceId() { jclass test_helper_class = app_framework::FindClass( env, activity, "com/google/firebase/example/TestHelper"); if (env->ExceptionCheck()) { + LogError("Couldn't find TestHelper class"); env->ExceptionDescribe(); env->ExceptionClear(); return ""; } jmethodID get_id = - env->GetMethodID(test_helper_class, "getDebugDeviceId", - "(Landroid/content/Context;)Ljava/lang/String;"); + env->GetStaticMethodID(test_helper_class, "getDebugDeviceId", + "(Landroid/content/Context;)Ljava/lang/String;"); + if (env->ExceptionCheck()) { + LogError("Couldn't look up getDebugDeviceId method"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return ""; + } jobject device_id_obj = env->CallStaticObjectMethod(test_helper_class, get_id, activity); if (env->ExceptionCheck()) { @@ -293,6 +300,7 @@ std::string FirebaseTest::GetDebugDeviceId() { jstring device_id_jstring = static_cast(device_id_obj); const char* device_id_text = env->GetStringUTFChars(device_id_jstring, nullptr); + LogError("Couldn't get debug device ID"); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); From 839e84a5440286ab95a45bf9fbe3edb3c160c957 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 15:51:59 -0700 Subject: [PATCH 26/56] Remove extra Enum method, and also format code. --- app/src/util_android.h | 2 -- gma/src/android/ump/consent_info_internal_android.cc | 9 +++++---- .../src/android/android_firebase_test_framework.cc | 5 ++++- .../java/com/google/firebase/example/TestHelper.java | 5 +---- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/util_android.h b/app/src/util_android.h index c5e82ca847..edd58d2cb5 100644 --- a/app/src/util_android.h +++ b/app/src/util_android.h @@ -614,8 +614,6 @@ METHOD_LOOKUP_DECLARATION(double_class, DOUBLE_METHODS); #define ENUM_METHODS(X) \ X(Equals, "equals", \ "(Ljava/lang/Object;)Z"), \ - X(Ordinal, "ordinal", \ - "()I"), \ X(Name, "name", \ "()Ljava/lang/String;") // clang-format on METHOD_LOOKUP_DECLARATION(enum_class, ENUM_METHODS); diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 6db7473048..a1b527b7e3 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -18,6 +18,7 @@ #include +#include #include #include "app/src/assert.h" @@ -48,18 +49,18 @@ firebase::Mutex ConsentInfoInternalAndroid::s_instance_mutex; "()I"), \ X(ShowPrivacyOptionsForm, "showPrivacyOptionsForm", \ "(JLandroid/app/Activity;)V"), \ - X(Reset, "reset", "()V"), \ + X(Reset, "reset", "()V"), \ X(CanRequestAds, "canRequestAds", "()Z"), \ - X(IsConsentFormAvailable, "isConsentFormAvailable", "()Z"), \ + X(IsConsentFormAvailable, "isConsentFormAvailable", "()Z"), \ X(Disconnect, "disconnect", "()V") // clang-format on // clang-format off #define CONSENTINFOHELPER_FIELDS(X) \ X(PrivacyOptionsRequirementUnknown, \ - "PRIVACY_OPTIONS_REQUIREMENT_UNKNOWN", "I", util::kFieldTypeStatic), \ + "PRIVACY_OPTIONS_REQUIREMENT_UNKNOWN", "I", util::kFieldTypeStatic), \ X(PrivacyOptionsRequirementRequired, \ - "PRIVACY_OPTIONS_REQUIREMENT_REQUIRED", "I", util::kFieldTypeStatic), \ + "PRIVACY_OPTIONS_REQUIREMENT_REQUIRED", "I", util::kFieldTypeStatic), \ X(PrivacyOptionsRequirementNotRequired, \ "PRIVACY_OPTIONS_REQUIREMENT_NOT_REQUIRED", "I", util::kFieldTypeStatic) // clang-format on diff --git a/testing/test_framework/src/android/android_firebase_test_framework.cc b/testing/test_framework/src/android/android_firebase_test_framework.cc index e675f4c4ba..c73bcfa97c 100644 --- a/testing/test_framework/src/android/android_firebase_test_framework.cc +++ b/testing/test_framework/src/android/android_firebase_test_framework.cc @@ -17,6 +17,7 @@ namespace firebase_test_framework { using app_framework::LogDebug; +using app_framework::LogInfo; using app_framework::LogError; // Blocking HTTP request helper function. @@ -300,14 +301,16 @@ std::string FirebaseTest::GetDebugDeviceId() { jstring device_id_jstring = static_cast(device_id_obj); const char* device_id_text = env->GetStringUTFChars(device_id_jstring, nullptr); - LogError("Couldn't get debug device ID"); + if (env->ExceptionCheck()) { + LogError("Couldn't get debug device ID"); env->ExceptionDescribe(); env->ExceptionClear(); return ""; } device_id = new char[strlen(device_id_text) + 1]; strcpy(device_id, device_id_text); // NOLINT + LogInfo("Got debug device ID: %s", device_id); env->ReleaseStringUTFChars(device_id_jstring, device_id_text); env->DeleteLocalRef(device_id_jstring); diff --git a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java index 7f76d04baa..36151b6b65 100644 --- a/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java +++ b/testing/test_framework/src/android/java/com/google/firebase/example/TestHelper.java @@ -82,13 +82,10 @@ private static String md5(String message) { } public static String getDebugDeviceId(Context context) { - if (isRunningOnEmulator()) { - return "emulator"; - } ContentResolver contentResolver = context.getContentResolver(); if (contentResolver == null) { return "error"; } - return Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID); + return md5(Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)); } } From 32b54b337f83de8ae423b085e60f32ea2c149fa2 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 16:00:01 -0700 Subject: [PATCH 27/56] Fix error message handling for Unavailable error. And format code. --- .../ump/consent_info_internal_android.cc | 5 +- .../ump/internal/cpp/ConsentInfoHelper.java | 54 ++++++++++++++----- .../android_firebase_test_framework.cc | 4 +- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index a1b527b7e3..1259da338a 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -257,8 +257,7 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, jobject helper_ref = env->NewObject( consent_info_helper::GetClass(), consent_info_helper::GetMethodId(consent_info_helper::kConstructor), - reinterpret_cast(this), - activity); + reinterpret_cast(this), activity); util::CheckAndClearJniExceptions(env); if (!helper_ref) { ReleaseClasses(env); @@ -369,7 +368,7 @@ static ConsentFormError CppConsentFormErrorFromAndroidFormError( return kConsentFormErrorInternal; if (error == g_enum_cache.formerror_timeout) return kConsentFormErrorTimeout; if (error == g_enum_cache.formerror_invalid_operation) { - if (message && strcasestr(message, "unavailable") != nullptr) + if (message && strcasestr(message, "No available form") != nullptr) return kConsentFormErrorUnavailable; else return kConsentFormErrorInvalidOperation; diff --git a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java index f52ab25f41..0758b06455 100644 --- a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java @@ -67,10 +67,12 @@ public class ConsentInfoHelper { public static final int FUNCTION_SHOW_PRIVACY_OPTIONS_FORM = 4; public ConsentInfoHelper(long consentInfoInternalPtr, Activity activity) { - mInternalPtr = consentInfoInternalPtr; - mActivity = activity; - // Test the callbacks and fail quickly if something's wrong. - completeFuture(-1, CPP_NULLPTR, CPP_NULLPTR, 0, null); + synchronized (mLock) { + mInternalPtr = consentInfoInternalPtr; + mActivity = activity; + // Test the callbacks and fail quickly if something's wrong. + completeFuture(-1, CPP_NULLPTR, CPP_NULLPTR, 0, null); + } } public int getConsentStatus() { @@ -80,8 +82,10 @@ public int getConsentStatus() { public void requestConsentInfoUpdate(final long futureHandle, boolean tagForUnderAgeOfConsent, int debugGeography, ArrayList debugIdList) { - if (mInternalPtr == 0) - return; + synchronized (mLock) { + if (mInternalPtr == 0) + return; + } final int functionId = FUNCTION_REQUEST_CONSENT_INFO_UPDATE; ConsentDebugSettings.Builder debugSettingsBuilder = null; @@ -116,6 +120,8 @@ public void run() { @Override public void onConsentInfoUpdateSuccess() { synchronized (mLock) { + if (mInternalPtr == 0) + return; completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } } @@ -124,6 +130,8 @@ public void onConsentInfoUpdateSuccess() { @Override public void onConsentInfoUpdateFailure(FormError formError) { synchronized (mLock) { + if (mInternalPtr == 0) + return; completeFuture(functionId, mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); } @@ -134,8 +142,10 @@ public void onConsentInfoUpdateFailure(FormError formError) { } public void loadConsentForm(final long futureHandle) { - if (mInternalPtr == 0) - return; + synchronized (mLock) { + if (mInternalPtr == 0) + return; + } final int functionId = FUNCTION_LOAD_CONSENT_FORM; mActivity.runOnUiThread(new Runnable() { @Override @@ -145,6 +155,8 @@ public void run() { @Override public void onConsentFormLoadSuccess(ConsentForm form) { synchronized (mLock) { + if (mInternalPtr == 0) + return; mConsentForm = form; completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } @@ -154,6 +166,8 @@ public void onConsentFormLoadSuccess(ConsentForm form) { @Override public void onConsentFormLoadFailure(FormError formError) { synchronized (mLock) { + if (mInternalPtr == 0) + return; mConsentForm = null; completeFuture(functionId, mInternalPtr, futureHandle, formError.getErrorCode(), formError.getMessage()); @@ -165,8 +179,10 @@ public void onConsentFormLoadFailure(FormError formError) { } public boolean showConsentForm(final long futureHandle, final Activity activity) { - if (mInternalPtr == 0) - return false; + synchronized (mLock) { + if (mInternalPtr == 0) + return; + } final int functionId = FUNCTION_SHOW_CONSENT_FORM; ConsentForm consentForm; synchronized (mLock) { @@ -185,6 +201,8 @@ public void run() { @Override public void onConsentFormDismissed(FormError formError) { synchronized (mLock) { + if (mInternalPtr == 0) + return; if (formError == null) { completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } else { @@ -201,8 +219,10 @@ public void onConsentFormDismissed(FormError formError) { } public void loadAndShowConsentFormIfRequired(final long futureHandle, final Activity activity) { - if (mInternalPtr == 0) - return; + synchronized (mLock) { + if (mInternalPtr == 0) + return; + } final int functionId = FUNCTION_LOAD_AND_SHOW_CONSENT_FORM_IF_REQUIRED; mActivity.runOnUiThread(new Runnable() { @Override @@ -212,6 +232,8 @@ activity, new OnConsentFormDismissedListener() { @Override public void onConsentFormDismissed(FormError formError) { synchronized (mLock) { + if (mInternalPtr == 0) + return; if (formError == null) { completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } else { @@ -231,8 +253,10 @@ public int getPrivacyOptionsRequirementStatus() { } public void showPrivacyOptionsForm(final long futureHandle, final Activity activity) { - if (mInternalPtr == 0) - return; + synchronized (mLock) { + if (mInternalPtr == 0) + return; + } final int functionId = FUNCTION_SHOW_PRIVACY_OPTIONS_FORM; mActivity.runOnUiThread(new Runnable() { @Override @@ -242,6 +266,8 @@ activity, new OnConsentFormDismissedListener() { @Override public void onConsentFormDismissed(FormError formError) { synchronized (mLock) { + if (mInternalPtr == 0) + return; if (formError == null) { completeFuture(functionId, mInternalPtr, futureHandle, 0, null); } else { diff --git a/testing/test_framework/src/android/android_firebase_test_framework.cc b/testing/test_framework/src/android/android_firebase_test_framework.cc index c73bcfa97c..2b5589a6aa 100644 --- a/testing/test_framework/src/android/android_firebase_test_framework.cc +++ b/testing/test_framework/src/android/android_firebase_test_framework.cc @@ -17,8 +17,8 @@ namespace firebase_test_framework { using app_framework::LogDebug; -using app_framework::LogInfo; using app_framework::LogError; +using app_framework::LogInfo; // Blocking HTTP request helper function. static bool SendHttpRequest(const char* url, @@ -283,7 +283,7 @@ std::string FirebaseTest::GetDebugDeviceId() { } jmethodID get_id = env->GetStaticMethodID(test_helper_class, "getDebugDeviceId", - "(Landroid/content/Context;)Ljava/lang/String;"); + "(Landroid/content/Context;)Ljava/lang/String;"); if (env->ExceptionCheck()) { LogError("Couldn't look up getDebugDeviceId method"); From 9cde0b95c5fa1853832c02fbe9ee2207713c40d4 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 16:09:38 -0700 Subject: [PATCH 28/56] Fix compile issue. --- .../com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java index 0758b06455..eb940377e7 100644 --- a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java @@ -181,7 +181,7 @@ public void onConsentFormLoadFailure(FormError formError) { public boolean showConsentForm(final long futureHandle, final Activity activity) { synchronized (mLock) { if (mInternalPtr == 0) - return; + return false; } final int functionId = FUNCTION_SHOW_CONSENT_FORM; ConsentForm consentForm; From 11729d699d71395913425c6c504551c5cd7902f4 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 16:14:58 -0700 Subject: [PATCH 29/56] Remove extra debugging output. --- .../src/android/android_firebase_test_framework.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/testing/test_framework/src/android/android_firebase_test_framework.cc b/testing/test_framework/src/android/android_firebase_test_framework.cc index 2b5589a6aa..8c37eec1a1 100644 --- a/testing/test_framework/src/android/android_firebase_test_framework.cc +++ b/testing/test_framework/src/android/android_firebase_test_framework.cc @@ -18,7 +18,6 @@ namespace firebase_test_framework { using app_framework::LogDebug; using app_framework::LogError; -using app_framework::LogInfo; // Blocking HTTP request helper function. static bool SendHttpRequest(const char* url, @@ -310,7 +309,6 @@ std::string FirebaseTest::GetDebugDeviceId() { } device_id = new char[strlen(device_id_text) + 1]; strcpy(device_id, device_id_text); // NOLINT - LogInfo("Got debug device ID: %s", device_id); env->ReleaseStringUTFChars(device_id_jstring, device_id_text); env->DeleteLocalRef(device_id_jstring); From 6b2363688e1782cb84c0be88a446828f196cd745 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 16:16:58 -0700 Subject: [PATCH 30/56] Remove test filter for UMP only. --- gma/integration_test/src/integration_test.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gma/integration_test/src/integration_test.cc b/gma/integration_test/src/integration_test.cc index ffbe0787f7..134397a8b9 100644 --- a/gma/integration_test/src/integration_test.cc +++ b/gma/integration_test/src/integration_test.cc @@ -354,8 +354,6 @@ void FirebaseGmaPreInitializationTests::SetUpTestSuite() { // Test cases below. -#if 0 // jsimantov - TEST_F(FirebaseGmaMinimalTest, TestInitializeGmaWithoutFirebase) { // Don't initialize mediation in this test so that a later test can still // verify that mediation has not been initialized. @@ -2475,8 +2473,6 @@ TEST_F(FirebaseGmaTest, TestAdViewMultithreadDeletion) { #endif // #if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && // TARGET_OS_IPHONE) -#endif // jsimantov - class FirebaseGmaUmpTest : public FirebaseGmaTest { public: FirebaseGmaUmpTest() : consent_info_(nullptr) {} From 97b2ff22426aae4b9a7eedb5cb6ccb1ae6dc023e Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 16:22:05 -0700 Subject: [PATCH 31/56] Remove extraneous HasExceptionOccurred function since ExceptionCheck does it for us. --- app/src/util_android.cc | 9 --------- app/src/util_android.h | 5 ----- .../android/ump/consent_info_internal_android.cc | 16 ++++++++-------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/app/src/util_android.cc b/app/src/util_android.cc index c052a8e7e0..1ece3f1797 100644 --- a/app/src/util_android.cc +++ b/app/src/util_android.cc @@ -1804,15 +1804,6 @@ std::string GetAndClearExceptionMessage(JNIEnv* env) { return std::string(); } -bool HasExceptionOccurred(JNIEnv* env) { - jobject exception = env->ExceptionOccurred(); - if (exception) { - env->DeleteLocalRef(exception); - return true; - } - return false; -} - std::string GetMessageFromException(JNIEnv* env, jobject exception) { if (exception != nullptr) { jstring message = static_cast(env->CallObjectMethod( diff --git a/app/src/util_android.h b/app/src/util_android.h index edd58d2cb5..722e81bbe0 100644 --- a/app/src/util_android.h +++ b/app/src/util_android.h @@ -1094,11 +1094,6 @@ bool CheckAndClearJniExceptions(JNIEnv* env); // Otherwise, it will return an empty string. std::string GetAndClearExceptionMessage(JNIEnv* env); -// Returns true if an JNI exception occurred, false otherwise. -// Does not clear the exception, so you can then call -// GetAndClearExceptionMessage(). -bool HasExceptionOccurred(JNIEnv* env); - // Returns the message from an exception. // This does not clear the JNI exception state. std::string GetMessageFromException(JNIEnv* env, jobject exception); diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 1259da338a..a6051c56fc 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -395,7 +395,7 @@ Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( future_handle, tag_for_under_age_of_consent, debug_geography, debug_device_ids_list); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { std::string exception_message = util::GetAndClearExceptionMessage(env); CompleteFuture(handle, kConsentRequestErrorInternal, exception_message.c_str()); @@ -415,7 +415,7 @@ ConsentStatus ConsentInfoInternalAndroid::GetConsentStatus() { jint result = env->CallIntMethod( helper_, consent_info_helper::GetMethodId(consent_info_helper::kGetConsentStatus)); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { util::CheckAndClearJniExceptions(env); return kConsentStatusUnknown; } @@ -430,7 +430,7 @@ ConsentFormStatus ConsentInfoInternalAndroid::GetConsentFormStatus() { jboolean is_available = env->CallBooleanMethod( helper_, consent_info_helper::GetMethodId( consent_info_helper::kIsConsentFormAvailable)); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { util::CheckAndClearJniExceptions(env); return kConsentFormStatusUnknown; } @@ -447,7 +447,7 @@ Future ConsentInfoInternalAndroid::LoadConsentForm() { helper_, consent_info_helper::GetMethodId(consent_info_helper::kLoadConsentForm), future_handle); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { std::string exception_message = util::GetAndClearExceptionMessage(env); CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); @@ -464,7 +464,7 @@ Future ConsentInfoInternalAndroid::ShowConsentForm(FormParent parent) { helper_, consent_info_helper::GetMethodId(consent_info_helper::kShowConsentForm), future_handle, parent); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { std::string exception_message = util::GetAndClearExceptionMessage(env); CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); @@ -490,7 +490,7 @@ Future ConsentInfoInternalAndroid::LoadAndShowConsentFormIfRequired( consent_info_helper::GetMethodId( consent_info_helper::kLoadAndShowConsentFormIfRequired), future_handle, parent); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { std::string exception_message = util::GetAndClearExceptionMessage(env); CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); @@ -523,7 +523,7 @@ Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( consent_info_helper::GetMethodId( consent_info_helper::kShowPrivacyOptionsForm), future_handle, parent); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { std::string exception_message = util::GetAndClearExceptionMessage(env); CompleteFuture(handle, kConsentFormErrorInternal, exception_message.c_str()); @@ -537,7 +537,7 @@ bool ConsentInfoInternalAndroid::CanRequestAds() { jboolean can_request = env->CallBooleanMethod( helper_, consent_info_helper::GetMethodId(consent_info_helper::kCanRequestAds)); - if (util::HasExceptionOccurred(env)) { + if (env->ExceptionCheck()) { util::CheckAndClearJniExceptions(env); return false; } From 56b320cca63a00bc4a9b2f34ba10855f5b9eb76c Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 17:52:40 -0700 Subject: [PATCH 32/56] Add OperationInProgress check to iOS SDK as well, and a test for it. Clean up enum cache and remove requirement to sync up c++/java enums. --- gma/integration_test/src/integration_test.cc | 85 ++++++ .../ump/consent_info_internal_android.cc | 268 +++++++++++------- .../ump/consent_info_internal_android.h | 50 +++- gma/src/ios/ump/consent_info_internal_ios.mm | 41 +++ .../ump/internal/cpp/ConsentInfoHelper.java | 7 +- 5 files changed, 347 insertions(+), 104 deletions(-) diff --git a/gma/integration_test/src/integration_test.cc b/gma/integration_test/src/integration_test.cc index 134397a8b9..1e05d08e21 100644 --- a/gma/integration_test/src/integration_test.cc +++ b/gma/integration_test/src/integration_test.cc @@ -2942,4 +2942,89 @@ TEST_F(FirebaseGmaUmpTest, TestUmpCleanupRaceCondition) { EXPECT_EQ(future_show.status(), firebase::kFutureStatusInvalid); } +TEST_F(FirebaseGmaUmpTest, TestUmpMethodsReturnOperationInProgress) { + SKIP_TEST_ON_DESKTOP; + + using firebase::gma::ump::ConsentFormStatus; + using firebase::gma::ump::ConsentRequestParameters; + using firebase::gma::ump::ConsentStatus; + + // Check that all of the UMP operations properly return an OperationInProgress + // error if called more than once at the same time. Each step of this test is + // inherently flaky, so add flaky test blocks all over. + + ConsentRequestParameters params; + params.tag_for_under_age_of_consent = false; + params.debug_settings.debug_geography = + firebase::gma::ump::kConsentDebugGeographyEEA; + params.debug_settings.debug_device_ids = kTestDeviceIDs; + params.debug_settings.debug_device_ids.push_back(GetDebugDeviceId()); + + FLAKY_TEST_SECTION_BEGIN(); + firebase::Future future_request_1 = + consent_info_->RequestConsentInfoUpdate(params); + firebase::Future future_request_2 = + consent_info_->RequestConsentInfoUpdate(params); + WaitForCompletion( + future_request_2, "RequestConsentInfoUpdate second", + firebase::gma::ump::kConsentRequestErrorOperationInProgress); + WaitForCompletion(future_request_1, "RequestConsentInfoUpdate first"); + FLAKY_TEST_SECTION_END(); + + FLAKY_TEST_SECTION_BEGIN(); + firebase::Future future_load_1 = consent_info_->LoadConsentForm(); + firebase::Future future_load_2 = consent_info_->LoadConsentForm(); + WaitForCompletion(future_load_2, "LoadConsentForm second", + firebase::gma::ump::kConsentFormErrorOperationInProgress); + WaitForCompletion(future_load_1, "LoadConsentForm first"); + FLAKY_TEST_SECTION_END(); + + if (ShouldRunUITests()) { + // The below should only be checked if UI tests are enabled, as they + // require some interaction. + FLAKY_TEST_SECTION_BEGIN(); + firebase::Future future_show_1 = + consent_info_->ShowConsentForm(app_framework::GetWindowController()); + firebase::Future future_show_2 = + consent_info_->ShowConsentForm(app_framework::GetWindowController()); + WaitForCompletion(future_show_2, "ShowConsentForm second", + firebase::gma::ump::kConsentFormErrorOperationInProgress); + WaitForCompletion(future_show_1, "ShowConsentForm first"); + FLAKY_TEST_SECTION_END(); + + FLAKY_TEST_SECTION_BEGIN(); + firebase::Future future_privacy_1 = + consent_info_->ShowPrivacyOptionsForm( + app_framework::GetWindowController()); + firebase::Future future_privacy_2 = + consent_info_->ShowPrivacyOptionsForm( + app_framework::GetWindowController()); + WaitForCompletion(future_privacy_2, "ShowPrivacyOptionsForm second", + firebase::gma::ump::kConsentFormErrorOperationInProgress); + WaitForCompletion(future_privacy_1, "ShowPrivacyOptionsForm first"); + FLAKY_TEST_SECTION_END(); + + consent_info_->Reset(); + // Request again so we can test LoadAndShowConsentFormIfRequired. + WaitForCompletion(consent_info_->RequestConsentInfoUpdate(params), + "RequestConsentInfoUpdate"); + + FLAKY_TEST_SECTION_BEGIN(); + firebase::Future future_load_and_show_1 = + consent_info_->LoadAndShowConsentFormIfRequired( + app_framework::GetWindowController()); + firebase::Future future_load_and_show_2 = + consent_info_->LoadAndShowConsentFormIfRequired( + app_framework::GetWindowController()); + WaitForCompletion(future_load_and_show_2, + "LoadAndShowConsentFormIfRequired second", + firebase::gma::ump::kConsentFormErrorOperationInProgress); + WaitForCompletion(future_load_and_show_1, + "LoadAndShowConsentFormIfRequired first"); + FLAKY_TEST_SECTION_END(); + } else { + LogInfo("Skipping methods that require user interaction."); + } +} + } // namespace firebase_testapp_automated diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index a6051c56fc..971f4084b4 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -62,7 +62,19 @@ firebase::Mutex ConsentInfoInternalAndroid::s_instance_mutex; X(PrivacyOptionsRequirementRequired, \ "PRIVACY_OPTIONS_REQUIREMENT_REQUIRED", "I", util::kFieldTypeStatic), \ X(PrivacyOptionsRequirementNotRequired, \ - "PRIVACY_OPTIONS_REQUIREMENT_NOT_REQUIRED", "I", util::kFieldTypeStatic) + "PRIVACY_OPTIONS_REQUIREMENT_NOT_REQUIRED", "I", util::kFieldTypeStatic), \ + X(FunctionRequestConsentInfoUpdate, \ + "FUNCTION_REQUEST_CONSENT_INFO_UPDATE", "I", util::kFieldTypeStatic), \ + X(FunctionLoadConsentForm, \ + "FUNCTION_LOAD_CONSENT_FORM", "I", util::kFieldTypeStatic), \ + X(FunctionShowConsentForm, \ + "FUNCTION_SHOW_CONSENT_FORM", "I", util::kFieldTypeStatic), \ + X(FunctionLoadAndShowConsentFormIfRequired, \ + "FUNCTION_LOAD_AND_SHOW_CONSENT_FORM_IF_REQUIRED", \ + "I", util::kFieldTypeStatic), \ + X(FunctionShowPrivacyOptionsForm, \ + "FUNCTION_SHOW_PRIVACY_OPTIONS_FORM", "I", util::kFieldTypeStatic), \ + X(FunctionCount, "FUNCTION_COUNT", "I", util::kFieldTypeStatic) // clang-format on METHOD_LOOKUP_DECLARATION(consent_info_helper, CONSENTINFOHELPER_METHODS, @@ -164,34 +176,58 @@ ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { class_namespace::GetFieldId(class_namespace::k##field_name)) // clang-format on -static struct { - jint consentstatus_unknown; - jint consentstatus_required; - jint consentstatus_not_required; - jint consentstatus_obtained; +void ConsentInfoInternalAndroid::CacheEnumValues(JNIEnv* env) { + // Cache enum values when the class loads, to avoid JNI lookups during + // callbacks later on when converting enums between Android and C++ values. + enums_.consentstatus_unknown = + ENUM_VALUE(consentinformation_consentstatus, Unknown); + enums_.consentstatus_required = + ENUM_VALUE(consentinformation_consentstatus, Required); + enums_.consentstatus_not_required = + ENUM_VALUE(consentinformation_consentstatus, NotRequired); + enums_.consentstatus_obtained = + ENUM_VALUE(consentinformation_consentstatus, Obtained); - jint formerror_success; - jint formerror_internal; - jint formerror_network; - jint formerror_invalid_operation; - jint formerror_timeout; + enums_.debug_geography_disabled = + ENUM_VALUE(consentdebugsettings_debuggeography, Disabled); + enums_.debug_geography_eea = + ENUM_VALUE(consentdebugsettings_debuggeography, EEA); + enums_.debug_geography_not_eea = + ENUM_VALUE(consentdebugsettings_debuggeography, NotEEA); - jint debug_geography_disabled; - jint debug_geography_eea; - jint debug_geography_not_eea; + enums_.formerror_success = 0; + enums_.formerror_internal = ENUM_VALUE(formerror_errorcode, InternalError); + enums_.formerror_network = ENUM_VALUE(formerror_errorcode, InternetError); + enums_.formerror_invalid_operation = + ENUM_VALUE(formerror_errorcode, InvalidOperation); + enums_.formerror_timeout = ENUM_VALUE(formerror_errorcode, TimeOut); + + enums_.privacy_options_requirement_unknown = + ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementUnknown); + enums_.privacy_options_requirement_required = + ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementRequired); + enums_.privacy_options_requirement_not_required = + ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementNotRequired); - jint privacy_options_requirement_unknown; - jint privacy_options_requirement_required; - jint privacy_options_requirement_not_required; -} g_enum_cache; + enums_.function_request_consent_info_update = + ENUM_VALUE(consent_info_helper, FunctionRequestConsentInfoUpdate); + enums_.function_load_consent_form = + ENUM_VALUE(consent_info_helper, FunctionLoadConsentForm); + enums_.function_show_consent_form = + ENUM_VALUE(consent_info_helper, FunctionShowConsentForm); + enums_.function_load_and_show_consent_form_if_required = + ENUM_VALUE(consent_info_helper, FunctionLoadAndShowConsentFormIfRequired); + enums_.function_show_privacy_options_form = + ENUM_VALUE(consent_info_helper, FunctionShowPrivacyOptionsForm); + enums_.function_count = ENUM_VALUE(consent_info_helper, FunctionCount); +} void ConsentInfoInternalAndroid::JNI_ConsentInfoHelper_completeFuture( JNIEnv* env, jclass clazz, jint future_fn, jlong consent_info_internal_ptr, jlong future_handle, jint error_code, jobject error_message_obj) { MutexLock lock(s_instance_mutex); - if (consent_info_internal_ptr == 0 || s_instance == 0 || future_fn < 0 || - future_fn >= kConsentInfoFnCount) { - // Calling this with a null pointer or invalid fn, or if there is no active + if (consent_info_internal_ptr == 0 || s_instance == 0) { + // Calling this with a null pointer, or if there is no active // instance, is a no-op, so just return. return; } @@ -207,8 +243,8 @@ void ConsentInfoInternalAndroid::JNI_ConsentInfoHelper_completeFuture( std::string error_message = error_message_obj ? util::JniStringToString(env, error_message_obj) : ""; instance->CompleteFutureFromJniCallback( - env, static_cast(future_fn), - static_cast(future_handle), static_cast(error_code), + env, future_fn, static_cast(future_handle), + static_cast(error_code), error_message.length() > 0 ? error_message.c_str() : nullptr); } @@ -273,111 +309,108 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, util::CheckAndClearJniExceptions(env); - // Cache enum values when the class loads, to avoid JNI lookups during - // callbacks later on when converting enums between Android and C++ values. - g_enum_cache.consentstatus_unknown = - ENUM_VALUE(consentinformation_consentstatus, Unknown); - g_enum_cache.consentstatus_required = - ENUM_VALUE(consentinformation_consentstatus, Required); - g_enum_cache.consentstatus_not_required = - ENUM_VALUE(consentinformation_consentstatus, NotRequired); - g_enum_cache.consentstatus_obtained = - ENUM_VALUE(consentinformation_consentstatus, Obtained); - - g_enum_cache.debug_geography_disabled = - ENUM_VALUE(consentdebugsettings_debuggeography, Disabled); - g_enum_cache.debug_geography_eea = - ENUM_VALUE(consentdebugsettings_debuggeography, EEA); - g_enum_cache.debug_geography_not_eea = - ENUM_VALUE(consentdebugsettings_debuggeography, NotEEA); - - g_enum_cache.formerror_success = 0; - g_enum_cache.formerror_internal = - ENUM_VALUE(formerror_errorcode, InternalError); - g_enum_cache.formerror_network = - ENUM_VALUE(formerror_errorcode, InternetError); - g_enum_cache.formerror_invalid_operation = - ENUM_VALUE(formerror_errorcode, InvalidOperation); - g_enum_cache.formerror_timeout = ENUM_VALUE(formerror_errorcode, TimeOut); - - g_enum_cache.privacy_options_requirement_unknown = - ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementUnknown); - g_enum_cache.privacy_options_requirement_required = - ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementRequired); - g_enum_cache.privacy_options_requirement_not_required = - ENUM_VALUE(consent_info_helper, PrivacyOptionsRequirementNotRequired); + CacheEnumValues(env); util::CheckAndClearJniExceptions(env); } -static ConsentStatus CppConsentStatusFromAndroid(jint status) { - if (status == g_enum_cache.consentstatus_unknown) - return kConsentStatusUnknown; - if (status == g_enum_cache.consentstatus_required) - return kConsentStatusRequired; - if (status == g_enum_cache.consentstatus_not_required) +ConsentStatus ConsentInfoInternalAndroid::CppConsentStatusFromAndroid( + jint status) { + if (status == enums().consentstatus_unknown) return kConsentStatusUnknown; + if (status == enums().consentstatus_required) return kConsentStatusRequired; + if (status == enums().consentstatus_not_required) return kConsentStatusNotRequired; - if (status == g_enum_cache.consentstatus_obtained) - return kConsentStatusObtained; + if (status == enums().consentstatus_obtained) return kConsentStatusObtained; + LogWarning("GMA: Unknown ConsentStatus returned by UMP Android SDK: %d", + (int)status); return kConsentStatusUnknown; } -static PrivacyOptionsRequirementStatus -CppPrivacyOptionsRequirementStatusFromAndroid(jint status) { - if (status == g_enum_cache.privacy_options_requirement_unknown) +PrivacyOptionsRequirementStatus +ConsentInfoInternalAndroid::CppPrivacyOptionsRequirementStatusFromAndroid( + jint status) { + if (status == enums().privacy_options_requirement_unknown) return kPrivacyOptionsRequirementStatusUnknown; - if (status == g_enum_cache.privacy_options_requirement_required) + if (status == enums().privacy_options_requirement_required) return kPrivacyOptionsRequirementStatusRequired; - if (status == g_enum_cache.privacy_options_requirement_not_required) + if (status == enums().privacy_options_requirement_not_required) return kPrivacyOptionsRequirementStatusNotRequired; + LogWarning( + "GMA: Unknown PrivacyOptionsRequirementStatus returned by UMP Android " + "SDK: %d", + (int)status); return kPrivacyOptionsRequirementStatusUnknown; } -static jint AndroidDebugGeographyFromCppDebugGeography( +jint ConsentInfoInternalAndroid::AndroidDebugGeographyFromCppDebugGeography( ConsentDebugGeography geo) { - // Cache values the first time this function runs. switch (geo) { case kConsentDebugGeographyDisabled: - return g_enum_cache.debug_geography_disabled; + return enums().debug_geography_disabled; case kConsentDebugGeographyEEA: - return g_enum_cache.debug_geography_eea; + return enums().debug_geography_eea; case kConsentDebugGeographyNonEEA: - return g_enum_cache.debug_geography_not_eea; + return enums().debug_geography_not_eea; default: - return g_enum_cache.debug_geography_disabled; + return enums().debug_geography_disabled; } } -static ConsentRequestError CppConsentRequestErrorFromAndroidFormError( - jint error, const char* message = nullptr) { - // Cache values the first time this function runs. - if (error == g_enum_cache.formerror_success) return kConsentRequestSuccess; - if (error == g_enum_cache.formerror_internal) - return kConsentRequestErrorInternal; - if (error == g_enum_cache.formerror_network) - return kConsentRequestErrorNetwork; - if (error == g_enum_cache.formerror_invalid_operation) - return kConsentRequestErrorInvalidOperation; +// Android uses FormError to report request errors as well. +ConsentRequestError +ConsentInfoInternalAndroid::CppConsentRequestErrorFromAndroidFormError( + jint error, const char* message) { + if (error == enums().formerror_success) return kConsentRequestSuccess; + if (error == enums().formerror_internal) return kConsentRequestErrorInternal; + if (error == enums().formerror_network) return kConsentRequestErrorNetwork; + if (error == enums().formerror_invalid_operation) { + // Error strings taken directly from the UMP Android SDK. + if (message && strcasestr(message, "misconfiguration") != nullptr) + return kConsentRequestErrorMisconfiguration; + else if (message && + strcasestr(message, "requires a valid application ID") != nullptr) + return kConsentRequestErrorInvalidAppId; + else + return kConsentRequestErrorInvalidOperation; + } + LogWarning("GMA: Unknown RequestError returned by UMP Android SDK: %d (%s)", + (int)error, message ? message : ""); return kConsentRequestErrorUnknown; } -static ConsentFormError CppConsentFormErrorFromAndroidFormError( - jint error, const char* message = nullptr) { - if (error == g_enum_cache.formerror_success) return kConsentFormSuccess; - if (error == g_enum_cache.formerror_internal) - return kConsentFormErrorInternal; - if (error == g_enum_cache.formerror_timeout) return kConsentFormErrorTimeout; - if (error == g_enum_cache.formerror_invalid_operation) { - if (message && strcasestr(message, "No available form") != nullptr) +ConsentFormError +ConsentInfoInternalAndroid::CppConsentFormErrorFromAndroidFormError( + jint error, const char* message) { + if (error == enums().formerror_success) return kConsentFormSuccess; + if (error == enums().formerror_internal) return kConsentFormErrorInternal; + if (error == enums().formerror_timeout) return kConsentFormErrorTimeout; + if (error == enums().formerror_invalid_operation) { + // Error strings taken directly from the UMP Android SDK. + if (message && strcasestr(message, "no available form") != nullptr) + return kConsentFormErrorUnavailable; + else if (message && strcasestr(message, "form is not required") != nullptr) return kConsentFormErrorUnavailable; + else if (message && + strcasestr(message, "can only be invoked once") != nullptr) + return kConsentFormErrorAlreadyUsed; else return kConsentFormErrorInvalidOperation; } + LogWarning("GMA: Unknown RequestError returned by UMP Android SDK: %d (%s)", + (int)error, message ? message : ""); return kConsentFormErrorUnknown; } Future ConsentInfoInternalAndroid::RequestConsentInfoUpdate( const ConsentRequestParameters& params) { + if (RequestConsentInfoUpdateLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentRequestErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnRequestConsentInfoUpdate); JNIEnv* env = GetJNIEnv(); @@ -439,6 +472,14 @@ ConsentFormStatus ConsentInfoInternalAndroid::GetConsentFormStatus() { } Future ConsentInfoInternalAndroid::LoadConsentForm() { + if (LoadConsentFormLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnLoadConsentForm); JNIEnv* env = GetJNIEnv(); jlong future_handle = static_cast(handle.get().id()); @@ -456,6 +497,14 @@ Future ConsentInfoInternalAndroid::LoadConsentForm() { } Future ConsentInfoInternalAndroid::ShowConsentForm(FormParent parent) { + if (ShowConsentFormLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnShowConsentForm); JNIEnv* env = GetJNIEnv(); @@ -479,6 +528,15 @@ Future ConsentInfoInternalAndroid::ShowConsentForm(FormParent parent) { Future ConsentInfoInternalAndroid::LoadAndShowConsentFormIfRequired( FormParent parent) { + if (LoadAndShowConsentFormIfRequiredLastResult().status() == + kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnLoadAndShowConsentFormIfRequired); @@ -513,6 +571,14 @@ ConsentInfoInternalAndroid::GetPrivacyOptionsRequirementStatus() { Future ConsentInfoInternalAndroid::ShowPrivacyOptionsForm( FormParent parent) { + if (ShowPrivacyOptionsFormLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnShowPrivacyOptionsForm); @@ -557,29 +623,27 @@ JNIEnv* ConsentInfoInternalAndroid::GetJNIEnv() { jobject ConsentInfoInternalAndroid::activity() { return activity_; } void ConsentInfoInternalAndroid::CompleteFutureFromJniCallback( - JNIEnv* env, ConsentInfoFn future_fn, FutureHandleId handle_id, - int java_error_code, const char* error_message) { + JNIEnv* env, jint future_fn, FutureHandleId handle_id, int java_error_code, + const char* error_message) { if (!futures()->ValidFuture(handle_id)) { // This future is no longer valid, so no need to complete it. return; } + if (future_fn < 0 || future_fn >= enums().function_count) { + // Called with an invalid function ID, ignore this callback. + return; + } FutureHandle raw_handle(handle_id); SafeFutureHandle handle(raw_handle); - if (future_fn == kConsentInfoFnRequestConsentInfoUpdate) { + if (future_fn == enums().function_request_consent_info_update) { // RequestConsentInfoUpdate uses the ConsentRequestError enum. ConsentRequestError error_code = CppConsentRequestErrorFromAndroidFormError( java_error_code, error_message); - if (error_message == nullptr) { - error_message = GetConsentRequestErrorMessage(error_code); - } CompleteFuture(handle, error_code, error_message); } else { // All other methods use the ConsentFormError enum. ConsentFormError error_code = CppConsentFormErrorFromAndroidFormError(java_error_code, error_message); - if (error_message == nullptr) { - error_message = GetConsentFormErrorMessage(error_code); - } CompleteFuture(handle, error_code, error_message); } } diff --git a/gma/src/android/ump/consent_info_internal_android.h b/gma/src/android/ump/consent_info_internal_android.h index faf474191c..4c0498671f 100644 --- a/gma/src/android/ump/consent_info_internal_android.h +++ b/gma/src/android/ump/consent_info_internal_android.h @@ -56,18 +56,66 @@ class ConsentInfoInternalAndroid : public ConsentInfoInternal { jobject activity(); private: + struct EnumCache { + jint consentstatus_unknown; + jint consentstatus_required; + jint consentstatus_not_required; + jint consentstatus_obtained; + + jint formerror_success; + jint formerror_internal; + jint formerror_network; + jint formerror_invalid_operation; + jint formerror_timeout; + + jint debug_geography_disabled; + jint debug_geography_eea; + jint debug_geography_not_eea; + + jint privacy_options_requirement_unknown; + jint privacy_options_requirement_required; + jint privacy_options_requirement_not_required; + + jint function_request_consent_info_update; + jint function_load_consent_form; + jint function_show_consent_form; + jint function_load_and_show_consent_form_if_required; + jint function_show_privacy_options_form; + jint function_count; + }; + + // JNI native method callback for ConsentInfoHelper.completeFuture. + // Calls CompleteFutureFromJniCallback() below. static void JNI_ConsentInfoHelper_completeFuture( JNIEnv* env, jclass clazz, jint future_fn, jlong consent_info_internal_ptr, jlong future_handle, jint error_code, jobject error_message_obj); - void CompleteFutureFromJniCallback(JNIEnv* env, ConsentInfoFn future_fn, + // Complete the given Future when called from JNI. + void CompleteFutureFromJniCallback(JNIEnv* env, jint future_fn, FutureHandleId handle_id, int error_code, const char* error_message); + // Cache Java enum field values in the struct below. + void CacheEnumValues(JNIEnv* env); + + // Enum conversion methods. + ConsentStatus CppConsentStatusFromAndroid(jint status); + PrivacyOptionsRequirementStatus CppPrivacyOptionsRequirementStatusFromAndroid( + jint status); + jint AndroidDebugGeographyFromCppDebugGeography(ConsentDebugGeography geo); + ConsentRequestError CppConsentRequestErrorFromAndroidFormError( + jint error, const char* message = nullptr); + ConsentFormError CppConsentFormErrorFromAndroidFormError( + jint error, const char* message = nullptr); + + const EnumCache& enums() { return enums_; } + static ConsentInfoInternalAndroid* s_instance; static firebase::Mutex s_instance_mutex; + EnumCache enums_; + JavaVM* java_vm_; jobject activity_; jobject helper_; diff --git a/gma/src/ios/ump/consent_info_internal_ios.mm b/gma/src/ios/ump/consent_info_internal_ios.mm index 08d2a0d004..266f554328 100644 --- a/gma/src/ios/ump/consent_info_internal_ios.mm +++ b/gma/src/ios/ump/consent_info_internal_ios.mm @@ -85,6 +85,14 @@ static ConsentFormError CppFormErrorFromIosFormError(NSInteger code) { Future ConsentInfoInternalIos::RequestConsentInfoUpdate( const ConsentRequestParameters& params) { + if (RequestConsentInfoUpdateLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentRequestErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnRequestConsentInfoUpdate); @@ -162,6 +170,14 @@ static ConsentFormError CppFormErrorFromIosFormError(NSInteger code) { } Future ConsentInfoInternalIos::LoadConsentForm() { + if (LoadConsentFormLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnLoadConsentForm); loaded_form_ = nil; @@ -191,6 +207,14 @@ static ConsentFormError CppFormErrorFromIosFormError(NSInteger code) { } Future ConsentInfoInternalIos::ShowConsentForm(FormParent parent) { + if (ShowConsentFormLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnShowConsentForm); if (!loaded_form_) { @@ -219,6 +243,15 @@ static ConsentFormError CppFormErrorFromIosFormError(NSInteger code) { Future ConsentInfoInternalIos::LoadAndShowConsentFormIfRequired( FormParent parent) { + if (LoadAndShowConsentFormIfRequiredLastResult().status() == + kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnLoadAndShowConsentFormIfRequired); @@ -260,6 +293,14 @@ static ConsentFormError CppFormErrorFromIosFormError(NSInteger code) { } Future ConsentInfoInternalIos::ShowPrivacyOptionsForm(FormParent parent) { + if (ShowPrivacyOptionsFormLastResult().status() == kFutureStatusPending) { + // This operation is already in progress. + // Return a future with an error - this will not override the Fn entry. + SafeFutureHandle error_handle = CreateFuture(); + CompleteFuture(error_handle, kConsentFormErrorOperationInProgress); + return MakeFuture(futures(), error_handle); + } + SafeFutureHandle handle = CreateFuture(kConsentInfoFnShowPrivacyOptionsForm); util::DispatchAsyncSafeMainQueue(^{ diff --git a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java index eb940377e7..d688389f3a 100644 --- a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java @@ -50,7 +50,7 @@ public class ConsentInfoHelper { // The loaded consent form, if any. private ConsentForm mConsentForm = null; - // Create our own local passthrough version of these Enum object values + // Create our own local passthrough version of these enum object values // as integers, to make it easier for the C++ SDK to access them. public static final int PRIVACY_OPTIONS_REQUIREMENT_UNKNOWN = PrivacyOptionsRequirementStatus.UNKNOWN.ordinal(); @@ -60,11 +60,15 @@ public class ConsentInfoHelper { PrivacyOptionsRequirementStatus.NOT_REQUIRED.ordinal(); // Enum values for tracking which function we are calling back. + // Ensure these are incremental starting at 0. + // These don't have to match ConsentInfoFn, as the C++ code will + // use these Java enums directly. public static final int FUNCTION_REQUEST_CONSENT_INFO_UPDATE = 0; public static final int FUNCTION_LOAD_CONSENT_FORM = 1; public static final int FUNCTION_SHOW_CONSENT_FORM = 2; public static final int FUNCTION_LOAD_AND_SHOW_CONSENT_FORM_IF_REQUIRED = 3; public static final int FUNCTION_SHOW_PRIVACY_OPTIONS_FORM = 4; + public static final int FUNCTION_COUNT = 5; public ConsentInfoHelper(long consentInfoInternalPtr, Activity activity) { synchronized (mLock) { @@ -90,6 +94,7 @@ public void requestConsentInfoUpdate(final long futureHandle, boolean tagForUnde ConsentDebugSettings.Builder debugSettingsBuilder = null; + // Only create and use debugSettingsBuilder if a debug option is set. if (debugGeography != ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_DISABLED) { debugSettingsBuilder = new ConsentDebugSettings.Builder(mActivity).setDebugGeography(debugGeography); From 589f57ae496191cd8703b041adad52e8c8154581 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 19:02:29 -0700 Subject: [PATCH 33/56] Remove lint abortOnError false --- gma/build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gma/build.gradle b/gma/build.gradle index 1df99b05b5..7eee3916f5 100644 --- a/gma/build.gradle +++ b/gma/build.gradle @@ -70,10 +70,6 @@ android { } } } - - lintOptions { - abortOnError false - } } dependencies { From 639c4893ce88385a121bfad4edde600d485f3dda Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 19:41:40 -0700 Subject: [PATCH 34/56] Try enabling parallel gradle builds for Windows. --- gma/integration_test/src/integration_test.cc | 2 ++ scripts/gha/build_testapps.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/gma/integration_test/src/integration_test.cc b/gma/integration_test/src/integration_test.cc index 1e05d08e21..04006b24a9 100644 --- a/gma/integration_test/src/integration_test.cc +++ b/gma/integration_test/src/integration_test.cc @@ -268,6 +268,7 @@ void FirebaseGmaTest::SetUp() { // debugging. They appear as a long string of hex characters. firebase::gma::RequestConfiguration request_configuration; request_configuration.test_device_ids = kTestDeviceIDs; + request_configuration.test_device_ids.push_back(GetDebugDeviceId()); firebase::gma::SetRequestConfiguration(request_configuration); } @@ -332,6 +333,7 @@ void FirebaseGmaUITest::SetUp() { // debugging. They appear as a long string of hex characters. firebase::gma::RequestConfiguration request_configuration; request_configuration.test_device_ids = kTestDeviceIDs; + request_configuration.test_device_ids.push_back(GetDebugDeviceId()); firebase::gma::SetRequestConfiguration(request_configuration); } diff --git a/scripts/gha/build_testapps.py b/scripts/gha/build_testapps.py index d10e7704ad..a9292002dd 100644 --- a/scripts/gha/build_testapps.py +++ b/scripts/gha/build_testapps.py @@ -525,6 +525,9 @@ def _build_android(project_dir, sdk_dir): f.write("http.keepAlive=false\n") f.write("maven.wagon.http.pool=false\n") f.write("maven.wagon.httpconnectionManager.ttlSeconds=120") + f.write("org.gradle.daemon=true") + f.write("org.gradle.parallel=true") + # This will log the versions of dependencies for debugging purposes. _run([gradlew, "dependencies", "--configuration", "debugCompileClasspath"]) _run([gradlew, "assembleDebug", "--stacktrace"]) From bfd31c1edfa62a0f89977ecb7a8a062de322546d Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 20:46:37 -0700 Subject: [PATCH 35/56] Restore script. --- scripts/gha/build_testapps.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/gha/build_testapps.py b/scripts/gha/build_testapps.py index a9292002dd..d10e7704ad 100644 --- a/scripts/gha/build_testapps.py +++ b/scripts/gha/build_testapps.py @@ -525,9 +525,6 @@ def _build_android(project_dir, sdk_dir): f.write("http.keepAlive=false\n") f.write("maven.wagon.http.pool=false\n") f.write("maven.wagon.httpconnectionManager.ttlSeconds=120") - f.write("org.gradle.daemon=true") - f.write("org.gradle.parallel=true") - # This will log the versions of dependencies for debugging purposes. _run([gradlew, "dependencies", "--configuration", "debugCompileClasspath"]) _run([gradlew, "assembleDebug", "--stacktrace"]) From cb321225414355fe6d90f89f555c57da60846ad6 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 20:49:47 -0700 Subject: [PATCH 36/56] Remove lint on error abort. --- gma/integration_test/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/gma/integration_test/build.gradle b/gma/integration_test/build.gradle index de93c89ab5..bbbfd2577d 100644 --- a/gma/integration_test/build.gradle +++ b/gma/integration_test/build.gradle @@ -73,9 +73,6 @@ android { proguardFile file('proguard.pro') } } - lintOptions { - abortOnError false - } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" From aafb299e3feee4d746083b12f352e29daa7ce2e5 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 20:54:35 -0700 Subject: [PATCH 37/56] Remove extra manifest file. --- gma/ump_resources/AndroidManifest.xml | 6 ------ gma/ump_resources/build.gradle | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 gma/ump_resources/AndroidManifest.xml diff --git a/gma/ump_resources/AndroidManifest.xml b/gma/ump_resources/AndroidManifest.xml deleted file mode 100644 index 02feeb8dfa..0000000000 --- a/gma/ump_resources/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/gma/ump_resources/build.gradle b/gma/ump_resources/build.gradle index 75e3153e8c..519988d860 100644 --- a/gma/ump_resources/build.gradle +++ b/gma/ump_resources/build.gradle @@ -36,7 +36,7 @@ android { sourceSets { main { - manifest.srcFile 'AndroidManifest.xml' + manifest.srcFile '../../android_build_files/AndroidManifest.xml' java { srcDirs = ['../src_java/com/google/firebase/ump/internal/cpp'] } From d6fc10b8b7a47cbf97e84245dd154d409dc3b963 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:10:06 -0700 Subject: [PATCH 38/56] Restore build.gradle for GMA library proper --- gma/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gma/build.gradle b/gma/build.gradle index 7eee3916f5..1df99b05b5 100644 --- a/gma/build.gradle +++ b/gma/build.gradle @@ -70,6 +70,10 @@ android { } } } + + lintOptions { + abortOnError false + } } dependencies { From 1f9c98a6d272540b5a02400140b8b4e4f845b169 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:23:58 -0700 Subject: [PATCH 39/56] Temporarily remove ump_resources to see if this fixes Windows build hang. --- gma/CMakeLists.txt | 16 ++++++++------ gma/build.gradle | 3 ++- gma/integration_test/build.gradle | 3 +++ .../ump/consent_info_internal_android.cc | 22 +++++++++++-------- settings.gradle | 3 ++- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/gma/CMakeLists.txt b/gma/CMakeLists.txt index 38f5ad4136..ce2f57c152 100644 --- a/gma/CMakeLists.txt +++ b/gma/CMakeLists.txt @@ -38,17 +38,19 @@ binary_to_array("gma_resources" "${FIREBASE_GEN_FILE_DIR}/gma") # Define the resource build needed for Android -firebase_cpp_gradle(":gma:ump_resources:generateDexJarRelease" - "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar") -binary_to_array("ump_resources" - "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar" - "firebase_ump" - "${FIREBASE_GEN_FILE_DIR}/gma") +# TODO(jsimantov): Temporarily remove to diagnose Windows build issue. +#firebase_cpp_gradle(":gma:ump_resources:generateDexJarRelease" +# "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar") +#binary_to_array("ump_resources" +# "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar" +# "firebase_ump" +# "${FIREBASE_GEN_FILE_DIR}/gma") # Source files used by the Android implementation. +# TODO(jsimantov): Temporarily remove to diagnose Windows build issue. +# ${ump_resources_source} set(android_SRCS ${gma_resources_source} - ${ump_resources_source} src/android/ump/consent_info_internal_android.cc src/android/ad_request_converter.cc src/android/ad_error_android.cc diff --git a/gma/build.gradle b/gma/build.gradle index 1df99b05b5..8875f84f30 100644 --- a/gma/build.gradle +++ b/gma/build.gradle @@ -85,5 +85,6 @@ apply from: "$rootDir/android_build_files/generate_proguard.gradle" project.afterEvaluate { generateProguardFile('gma') setupDexDependencies(':gma:gma_resources') - setupDexDependencies(':gma:ump_resources') + // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. + // setupDexDependencies(':gma:ump_resources') } diff --git a/gma/integration_test/build.gradle b/gma/integration_test/build.gradle index bbbfd2577d..de93c89ab5 100644 --- a/gma/integration_test/build.gradle +++ b/gma/integration_test/build.gradle @@ -73,6 +73,9 @@ android { proguardFile file('proguard.pro') } } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 971f4084b4..e731b18c9c 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -25,7 +25,8 @@ #include "app/src/thread.h" #include "app/src/util_android.h" #include "firebase/internal/common.h" -#include "gma/ump_resources.h" +// TODO(jsimantov): Temporarily remove to diagnose Windows build issue. +// #include "gma/ump_resources.h" namespace firebase { namespace gma { @@ -261,14 +262,17 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, util::Initialize(env, activity); env->GetJavaVM(&java_vm_); - const std::vector embedded_files = - util::CacheEmbeddedFiles(env, activity, - firebase::internal::EmbeddedFile::ToVector( - firebase_ump::ump_resources_filename, - firebase_ump::ump_resources_data, - firebase_ump::ump_resources_size)); - if (!(consent_info_helper::CacheClassFromFiles(env, activity, - &embedded_files) != nullptr && + // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. + // const std::vector embedded_files = + // util::CacheEmbeddedFiles(env, activity, + // firebase::internal::EmbeddedFile::ToVector( + // firebase_ump::ump_resources_filename, + // firebase_ump::ump_resources_data, + // firebase_ump::ump_resources_size)); + if ( + // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. + // !(consent_info_helper::CacheClassFromFiles(env, activity, + // &embedded_files) != nullptr && consent_info_helper::CacheMethodIds(env, activity) && consent_info_helper::CacheFieldIds(env, activity) && consentinformation_consentstatus::CacheFieldIds(env, activity) && diff --git a/settings.gradle b/settings.gradle index debbf0555f..b4cbd453bc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'firebase_cpp_sdk' +// TODO(jsimantov): Temporarily remove to diagnose Windows build issue. +// ':gma:ump_resources', include ':app', ':app:app_resources', ':app:google_api_resources', @@ -16,7 +18,6 @@ include ':app', ':functions', ':gma', ':gma:gma_resources', - ':gma:ump_resources', ':installations', ':messaging', ':messaging:messaging_java', From 02778b472b8fcfe3f3ea07cf2cc3b8a77f544eac Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:26:54 -0700 Subject: [PATCH 40/56] Remove Flatbuffers patch to diagnose Windows hang. --- messaging/messaging_java/build.gradle | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/messaging/messaging_java/build.gradle b/messaging/messaging_java/build.gradle index 9c5826bb6b..2b9252d5fb 100644 --- a/messaging/messaging_java/build.gradle +++ b/messaging/messaging_java/build.gradle @@ -81,14 +81,15 @@ afterEvaluate { 'https://github.com/google/flatbuffers.git', flatbuffersDir } - exec { - executable 'git' - args 'apply', - '../../scripts/git/patches/flatbuffers/0001-remove-unused-var.patch', - '--verbose', - '--directory', - 'messaging/messaging_java/build/flatbuffers' - } + // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. + // exec { + // executable 'git' + // args 'apply', + // '../../scripts/git/patches/flatbuffers/0001-remove-unused-var.patch', + // '--verbose', + // '--directory', + // 'messaging/messaging_java/build/flatbuffers' + // } } // Locate or build flatc. From c8045bc96b49359c66ad61d5bb1bbd17f60b85ec Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:29:55 -0700 Subject: [PATCH 41/56] Fix build error. --- gma/src/android/ump/consent_info_internal_android.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index e731b18c9c..8f7fe0a6dc 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -269,7 +269,7 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, // firebase_ump::ump_resources_filename, // firebase_ump::ump_resources_data, // firebase_ump::ump_resources_size)); - if ( + if (!( // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. // !(consent_info_helper::CacheClassFromFiles(env, activity, // &embedded_files) != nullptr && From 03deccad8ca163bd8daca57413aa39df596deef9 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:41:55 -0700 Subject: [PATCH 42/56] Add missing dependencies. --- gma/ump_resources/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gma/ump_resources/build.gradle b/gma/ump_resources/build.gradle index 519988d860..820dd99f41 100644 --- a/gma/ump_resources/build.gradle +++ b/gma/ump_resources/build.gradle @@ -45,6 +45,9 @@ android { } dependencies { + implementation platform('com.google.firebase:firebase-bom:32.2.2') + implementation 'com.google.firebase:firebase-analytics' + implementation 'com.google.android.gms:play-services-ads:22.2.0' implementation 'com.google.android.ump:user-messaging-platform:2.1.0' } From 695f723d12cadf2ed88a4a98f27e4dd2a5c65130 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:51:29 -0700 Subject: [PATCH 43/56] Revert "Add missing dependencies." This reverts commit 03deccad8ca163bd8daca57413aa39df596deef9. --- gma/ump_resources/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/gma/ump_resources/build.gradle b/gma/ump_resources/build.gradle index 820dd99f41..519988d860 100644 --- a/gma/ump_resources/build.gradle +++ b/gma/ump_resources/build.gradle @@ -45,9 +45,6 @@ android { } dependencies { - implementation platform('com.google.firebase:firebase-bom:32.2.2') - implementation 'com.google.firebase:firebase-analytics' - implementation 'com.google.android.gms:play-services-ads:22.2.0' implementation 'com.google.android.ump:user-messaging-platform:2.1.0' } From f45196f283317cdfa7abcfd78308dd8ad51a8d46 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:51:44 -0700 Subject: [PATCH 44/56] Add ump-resources back in. --- gma/CMakeLists.txt | 16 +++++++--------- gma/build.gradle | 3 +-- settings.gradle | 3 +-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/gma/CMakeLists.txt b/gma/CMakeLists.txt index ce2f57c152..38f5ad4136 100644 --- a/gma/CMakeLists.txt +++ b/gma/CMakeLists.txt @@ -38,19 +38,17 @@ binary_to_array("gma_resources" "${FIREBASE_GEN_FILE_DIR}/gma") # Define the resource build needed for Android -# TODO(jsimantov): Temporarily remove to diagnose Windows build issue. -#firebase_cpp_gradle(":gma:ump_resources:generateDexJarRelease" -# "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar") -#binary_to_array("ump_resources" -# "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar" -# "firebase_ump" -# "${FIREBASE_GEN_FILE_DIR}/gma") +firebase_cpp_gradle(":gma:ump_resources:generateDexJarRelease" + "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar") +binary_to_array("ump_resources" + "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar" + "firebase_ump" + "${FIREBASE_GEN_FILE_DIR}/gma") # Source files used by the Android implementation. -# TODO(jsimantov): Temporarily remove to diagnose Windows build issue. -# ${ump_resources_source} set(android_SRCS ${gma_resources_source} + ${ump_resources_source} src/android/ump/consent_info_internal_android.cc src/android/ad_request_converter.cc src/android/ad_error_android.cc diff --git a/gma/build.gradle b/gma/build.gradle index 8875f84f30..1df99b05b5 100644 --- a/gma/build.gradle +++ b/gma/build.gradle @@ -85,6 +85,5 @@ apply from: "$rootDir/android_build_files/generate_proguard.gradle" project.afterEvaluate { generateProguardFile('gma') setupDexDependencies(':gma:gma_resources') - // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. - // setupDexDependencies(':gma:ump_resources') + setupDexDependencies(':gma:ump_resources') } diff --git a/settings.gradle b/settings.gradle index b4cbd453bc..debbf0555f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,4 @@ rootProject.name = 'firebase_cpp_sdk' -// TODO(jsimantov): Temporarily remove to diagnose Windows build issue. -// ':gma:ump_resources', include ':app', ':app:app_resources', ':app:google_api_resources', @@ -18,6 +16,7 @@ include ':app', ':functions', ':gma', ':gma:gma_resources', + ':gma:ump_resources', ':installations', ':messaging', ':messaging:messaging_java', From da195baea1917d2db62f16a6bfdb42a6402705e0 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:52:13 -0700 Subject: [PATCH 45/56] Add code back in. --- .../ump/consent_info_internal_android.cc | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 8f7fe0a6dc..feabb0b3ff 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -25,8 +25,7 @@ #include "app/src/thread.h" #include "app/src/util_android.h" #include "firebase/internal/common.h" -// TODO(jsimantov): Temporarily remove to diagnose Windows build issue. -// #include "gma/ump_resources.h" +#include "gma/ump_resources.h" namespace firebase { namespace gma { @@ -262,17 +261,14 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, util::Initialize(env, activity); env->GetJavaVM(&java_vm_); - // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. - // const std::vector embedded_files = - // util::CacheEmbeddedFiles(env, activity, - // firebase::internal::EmbeddedFile::ToVector( - // firebase_ump::ump_resources_filename, - // firebase_ump::ump_resources_data, - // firebase_ump::ump_resources_size)); - if (!( - // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. - // !(consent_info_helper::CacheClassFromFiles(env, activity, - // &embedded_files) != nullptr && +const std::vector embedded_files = + util::CacheEmbeddedFiles(env, activity, + firebase::internal::EmbeddedFile::ToVector( + firebase_ump::ump_resources_filename, + firebase_ump::ump_resources_data, + firebase_ump::ump_resources_size)); + if (!(consent_info_helper::CacheClassFromFiles(env, activity, + &embedded_files) != nullptr && consent_info_helper::CacheMethodIds(env, activity) && consent_info_helper::CacheFieldIds(env, activity) && consentinformation_consentstatus::CacheFieldIds(env, activity) && From 8105db135587ac85000dc7f1985ef714f526eb8b Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:53:14 -0700 Subject: [PATCH 46/56] Modify comment. --- .../android/ump/consent_info_internal_android.cc | 14 +++++++------- messaging/messaging_java/build.gradle | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index feabb0b3ff..971f4084b4 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -261,14 +261,14 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, util::Initialize(env, activity); env->GetJavaVM(&java_vm_); -const std::vector embedded_files = - util::CacheEmbeddedFiles(env, activity, - firebase::internal::EmbeddedFile::ToVector( - firebase_ump::ump_resources_filename, - firebase_ump::ump_resources_data, - firebase_ump::ump_resources_size)); + const std::vector embedded_files = + util::CacheEmbeddedFiles(env, activity, + firebase::internal::EmbeddedFile::ToVector( + firebase_ump::ump_resources_filename, + firebase_ump::ump_resources_data, + firebase_ump::ump_resources_size)); if (!(consent_info_helper::CacheClassFromFiles(env, activity, - &embedded_files) != nullptr && + &embedded_files) != nullptr && consent_info_helper::CacheMethodIds(env, activity) && consent_info_helper::CacheFieldIds(env, activity) && consentinformation_consentstatus::CacheFieldIds(env, activity) && diff --git a/messaging/messaging_java/build.gradle b/messaging/messaging_java/build.gradle index 2b9252d5fb..ec1616a7cc 100644 --- a/messaging/messaging_java/build.gradle +++ b/messaging/messaging_java/build.gradle @@ -81,7 +81,7 @@ afterEvaluate { 'https://github.com/google/flatbuffers.git', flatbuffersDir } - // TODO(jsimantov): Temporarily remove to diagnose Windows build issue. + // TODO(jsimantov): Investigate why this causes a build hang on Windows. // exec { // executable 'git' // args 'apply', From 194d08977358c411f3903eea5e6b9dd93a7e3f1b Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 21:53:43 -0700 Subject: [PATCH 47/56] Remove lint abort. --- gma/integration_test/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/gma/integration_test/build.gradle b/gma/integration_test/build.gradle index de93c89ab5..bbbfd2577d 100644 --- a/gma/integration_test/build.gradle +++ b/gma/integration_test/build.gradle @@ -73,9 +73,6 @@ android { proguardFile file('proguard.pro') } } - lintOptions { - abortOnError false - } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" From 3a348d6c66ee3c6392a11ece296e2509be03ec52 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 22:49:00 -0700 Subject: [PATCH 48/56] Add compatibility version. --- gma/gma_resources/build.gradle | 5 +++++ gma/ump_resources/build.gradle | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/gma/gma_resources/build.gradle b/gma/gma_resources/build.gradle index 79dc1bb0a8..adbd880daf 100644 --- a/gma/gma_resources/build.gradle +++ b/gma/gma_resources/build.gradle @@ -32,6 +32,11 @@ allprojects { apply plugin: 'com.android.library' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + compileSdkVersion 28 sourceSets { diff --git a/gma/ump_resources/build.gradle b/gma/ump_resources/build.gradle index 519988d860..5e5ea9139b 100644 --- a/gma/ump_resources/build.gradle +++ b/gma/ump_resources/build.gradle @@ -32,6 +32,10 @@ allprojects { apply plugin: 'com.android.library' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 sourceSets { From 41794c0d669e60279307ab6420e2268d20e52195 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 18 Aug 2023 23:00:54 -0700 Subject: [PATCH 49/56] Move ConsentInfoHelper class into regular gma_resources. No need for a separate ump_resources. --- gma/CMakeLists.txt | 9 --- gma/build.gradle | 1 - gma/gma_resources/build.gradle | 1 + .../ump/consent_info_internal_android.cc | 10 ++-- .../internal/cpp/ConsentInfoHelper.java | 2 +- gma/ump_resources/build.gradle | 60 ------------------- messaging/messaging_java/build.gradle | 16 ++--- settings.gradle | 1 - 8 files changed, 15 insertions(+), 85 deletions(-) rename gma/src_java/com/google/firebase/{ump => gma}/internal/cpp/ConsentInfoHelper.java (99%) delete mode 100644 gma/ump_resources/build.gradle diff --git a/gma/CMakeLists.txt b/gma/CMakeLists.txt index 38f5ad4136..a58354f7f9 100644 --- a/gma/CMakeLists.txt +++ b/gma/CMakeLists.txt @@ -37,18 +37,9 @@ binary_to_array("gma_resources" "firebase_gma" "${FIREBASE_GEN_FILE_DIR}/gma") -# Define the resource build needed for Android -firebase_cpp_gradle(":gma:ump_resources:generateDexJarRelease" - "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar") -binary_to_array("ump_resources" - "${CMAKE_CURRENT_LIST_DIR}/ump_resources/build/ump_resources_lib.jar" - "firebase_ump" - "${FIREBASE_GEN_FILE_DIR}/gma") - # Source files used by the Android implementation. set(android_SRCS ${gma_resources_source} - ${ump_resources_source} src/android/ump/consent_info_internal_android.cc src/android/ad_request_converter.cc src/android/ad_error_android.cc diff --git a/gma/build.gradle b/gma/build.gradle index 1df99b05b5..c0c2687bd1 100644 --- a/gma/build.gradle +++ b/gma/build.gradle @@ -85,5 +85,4 @@ apply from: "$rootDir/android_build_files/generate_proguard.gradle" project.afterEvaluate { generateProguardFile('gma') setupDexDependencies(':gma:gma_resources') - setupDexDependencies(':gma:ump_resources') } diff --git a/gma/gma_resources/build.gradle b/gma/gma_resources/build.gradle index adbd880daf..49d71d91ac 100644 --- a/gma/gma_resources/build.gradle +++ b/gma/gma_resources/build.gradle @@ -53,6 +53,7 @@ dependencies { implementation platform('com.google.firebase:firebase-bom:32.2.2') implementation 'com.google.firebase:firebase-analytics' implementation 'com.google.android.gms:play-services-ads:22.2.0' + implementation 'com.google.android.ump:user-messaging-platform:2.1.0' } afterEvaluate { diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 971f4084b4..2363db2cae 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -25,7 +25,7 @@ #include "app/src/thread.h" #include "app/src/util_android.h" #include "firebase/internal/common.h" -#include "gma/ump_resources.h" +#include "gma/gma_resources.h" namespace firebase { namespace gma { @@ -82,7 +82,7 @@ METHOD_LOOKUP_DECLARATION(consent_info_helper, CONSENTINFOHELPER_METHODS, METHOD_LOOKUP_DEFINITION( consent_info_helper, - "com/google/firebase/ump/internal/cpp/ConsentInfoHelper", + "com/google/firebase/gma/internal/cpp/ConsentInfoHelper", CONSENTINFOHELPER_METHODS, CONSENTINFOHELPER_FIELDS); // clang-format off @@ -264,9 +264,9 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, const std::vector embedded_files = util::CacheEmbeddedFiles(env, activity, firebase::internal::EmbeddedFile::ToVector( - firebase_ump::ump_resources_filename, - firebase_ump::ump_resources_data, - firebase_ump::ump_resources_size)); + firebase_gma::gma_resources_filename, + firebase_gma::gma_resources_data, + firebase_gma::gma_resources_size)); if (!(consent_info_helper::CacheClassFromFiles(env, activity, &embedded_files) != nullptr && consent_info_helper::CacheMethodIds(env, activity) && diff --git a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java similarity index 99% rename from gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java rename to gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java index d688389f3a..4c95a97b6b 100644 --- a/gma/src_java/com/google/firebase/ump/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.firebase.ump.internal.cpp; +package com.google.firebase.gma.internal.cpp; import android.app.Activity; import android.content.Context; diff --git a/gma/ump_resources/build.gradle b/gma/ump_resources/build.gradle deleted file mode 100644 index 5e5ea9139b..0000000000 --- a/gma/ump_resources/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2021 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. - -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' - classpath 'com.google.gms:google-services:4.2.0' - } -} -allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 - } - compileSdkVersion 28 - - sourceSets { - main { - manifest.srcFile '../../android_build_files/AndroidManifest.xml' - java { - srcDirs = ['../src_java/com/google/firebase/ump/internal/cpp'] - } - } - } -} - -dependencies { - implementation 'com.google.android.ump:user-messaging-platform:2.1.0' -} - -afterEvaluate { - generateReleaseBuildConfig.enabled = false -} - -apply from: "$rootDir/android_build_files/extract_and_dex.gradle" -extractAndDexAarFile('ump_resources') diff --git a/messaging/messaging_java/build.gradle b/messaging/messaging_java/build.gradle index ec1616a7cc..80a7d90776 100644 --- a/messaging/messaging_java/build.gradle +++ b/messaging/messaging_java/build.gradle @@ -82,14 +82,14 @@ afterEvaluate { flatbuffersDir } // TODO(jsimantov): Investigate why this causes a build hang on Windows. - // exec { - // executable 'git' - // args 'apply', - // '../../scripts/git/patches/flatbuffers/0001-remove-unused-var.patch', - // '--verbose', - // '--directory', - // 'messaging/messaging_java/build/flatbuffers' - // } + exec { + executable 'git' + args 'apply', + '../../scripts/git/patches/flatbuffers/0001-remove-unused-var.patch', + '--verbose', + '--directory', + 'messaging/messaging_java/build/flatbuffers' + } } // Locate or build flatc. diff --git a/settings.gradle b/settings.gradle index debbf0555f..0ec81050fc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,7 +16,6 @@ include ':app', ':functions', ':gma', ':gma:gma_resources', - ':gma:ump_resources', ':installations', ':messaging', ':messaging:messaging_java', From c68059ada22df19b69b17708bc04c0172016e84c Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Sat, 19 Aug 2023 07:15:44 -0700 Subject: [PATCH 50/56] Remove comment about build hang because spoiler alert, it doesn't cause it. --- messaging/messaging_java/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/messaging/messaging_java/build.gradle b/messaging/messaging_java/build.gradle index 80a7d90776..9cf3e3acc9 100644 --- a/messaging/messaging_java/build.gradle +++ b/messaging/messaging_java/build.gradle @@ -81,7 +81,6 @@ afterEvaluate { 'https://github.com/google/flatbuffers.git', flatbuffersDir } - // TODO(jsimantov): Investigate why this causes a build hang on Windows. exec { executable 'git' args 'apply', From 65af440f80000f29282577440df5fcb9cdf5ece1 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Sat, 19 Aug 2023 08:17:00 -0700 Subject: [PATCH 51/56] Ensure that GMA and UMP only load their shared class files once. --- gma/src/android/gma_android.cc | 28 +++++++++++++++---- gma/src/android/gma_android.h | 12 ++++++++ .../ump/consent_info_internal_android.cc | 25 +++++++++++++---- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/gma/src/android/gma_android.cc b/gma/src/android/gma_android.cc index bcc7e35949..af6a263fd0 100644 --- a/gma/src/android/gma_android.cc +++ b/gma/src/android/gma_android.cc @@ -49,6 +49,12 @@ namespace firebase { namespace gma { +namespace internal { +::firebase::Mutex g_cached_gma_classes_mutex; +std::vector<::firebase::internal::EmbeddedFile>* g_cached_gma_embedded_files = + nullptr; +} // namespace internal + METHOD_LOOKUP_DEFINITION(mobile_ads, PROGUARD_KEEP_CLASS "com/google/android/gms/ads/MobileAds", @@ -308,12 +314,22 @@ Future Initialize(JNIEnv* env, jobject activity, return Future(); } - const std::vector embedded_files = - util::CacheEmbeddedFiles(env, activity, - firebase::internal::EmbeddedFile::ToVector( - firebase_gma::gma_resources_filename, - firebase_gma::gma_resources_data, - firebase_gma::gma_resources_size)); + // Between this and UMP, we only want to load these files once. + { + MutexLock lock(internal::g_cached_gma_embedded_files_mutex); + if (internal::g_cached_gma_embedded_files == nullptr) { + internal::g_cached_gma_embedded_files = + new std::vector(); + *internal::g_cached_gma_embedded_files = + util::CacheEmbeddedFiles(env, activity, + firebase::internal::EmbeddedFile::ToVector( + firebase_gma::gma_resources_filename, + firebase_gma::gma_resources_data, + firebase_gma::gma_resources_size)); + } + } + const std::vector& embedded_files = + *internal::g_cached_gma_embedded_files; if (!(mobile_ads::CacheMethodIds(env, activity) && ad_request_builder::CacheMethodIds(env, activity) && diff --git a/gma/src/android/gma_android.h b/gma/src/android/gma_android.h index f2a38f9d21..8ada58c3d2 100644 --- a/gma/src/android/gma_android.h +++ b/gma/src/android/gma_android.h @@ -19,7 +19,11 @@ #include +#include + +#include "app/src/embedded_file.h" #include "app/src/util_android.h" +#include "firebase/internal/mutex.h" #include "gma/src/common/gma_common.h" namespace firebase { @@ -189,6 +193,14 @@ void ReleaseClasses(JNIEnv* env); jobject CreateJavaAdSize(JNIEnv* env, jobject activity, const AdSize& an_ad_size); +namespace internal { +// GMA and UMP share embedded dex files; this ensures +// that they are only loaded once each run. +extern ::firebase::Mutex g_cached_gma_embedded_files_mutex; +extern std::vector<::firebase::internal::EmbeddedFile>* + g_cached_gma_embedded_files; +} // namespace internal + } // namespace gma } // namespace firebase diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index 2363db2cae..e9f320cca0 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -26,6 +26,7 @@ #include "app/src/util_android.h" #include "firebase/internal/common.h" #include "gma/gma_resources.h" +#include "gma/src/android/gma_android.h" namespace firebase { namespace gma { @@ -261,12 +262,24 @@ ConsentInfoInternalAndroid::ConsentInfoInternalAndroid(JNIEnv* env, util::Initialize(env, activity); env->GetJavaVM(&java_vm_); - const std::vector embedded_files = - util::CacheEmbeddedFiles(env, activity, - firebase::internal::EmbeddedFile::ToVector( - firebase_gma::gma_resources_filename, - firebase_gma::gma_resources_data, - firebase_gma::gma_resources_size)); + // Between this and GMA, we only want to load these files once. + { + MutexLock lock( + ::firebase::gma::internal::g_cached_gma_embedded_files_mutex); + if (::firebase::gma::internal::g_cached_gma_embedded_files == nullptr) { + ::firebase::gma::internal::g_cached_gma_embedded_files = + new std::vector(); + *::firebase::gma::internal::g_cached_gma_embedded_files = + util::CacheEmbeddedFiles(env, activity, + firebase::internal::EmbeddedFile::ToVector( + firebase_gma::gma_resources_filename, + firebase_gma::gma_resources_data, + firebase_gma::gma_resources_size)); + } + } + const std::vector& embedded_files = + *::firebase::gma::internal::g_cached_gma_embedded_files; + if (!(consent_info_helper::CacheClassFromFiles(env, activity, &embedded_files) != nullptr && consent_info_helper::CacheMethodIds(env, activity) && From 67d4cf855b65f406896e38e63d33c691de2f5509 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Sat, 19 Aug 2023 08:32:46 -0700 Subject: [PATCH 52/56] Rename mutex. --- gma/src/android/gma_android.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gma/src/android/gma_android.cc b/gma/src/android/gma_android.cc index af6a263fd0..8ce19c7295 100644 --- a/gma/src/android/gma_android.cc +++ b/gma/src/android/gma_android.cc @@ -50,7 +50,7 @@ namespace firebase { namespace gma { namespace internal { -::firebase::Mutex g_cached_gma_classes_mutex; +::firebase::Mutex g_cached_gma_embedded_files_mutex; std::vector<::firebase::internal::EmbeddedFile>* g_cached_gma_embedded_files = nullptr; } // namespace internal From 6225dcf691b0dac03e88775ada37555186537aec Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Tue, 12 Sep 2023 13:15:46 -0700 Subject: [PATCH 53/56] Update copyright date and fix lock issue. Also disable lint warning in this testapp (temporarily?) --- gma/integration_test/build.gradle | 3 +++ gma/src/android/ump/consent_info_internal_android.cc | 8 +++++--- .../firebase/gma/internal/cpp/ConsentInfoHelper.java | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/gma/integration_test/build.gradle b/gma/integration_test/build.gradle index bbbfd2577d..fd65c3885e 100644 --- a/gma/integration_test/build.gradle +++ b/gma/integration_test/build.gradle @@ -73,6 +73,9 @@ android { proguardFile file('proguard.pro') } } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index e9f320cca0..a8c1844282 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -154,8 +154,10 @@ static void ReleaseClasses(JNIEnv* env) { } ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { - MutexLock lock(s_instance_mutex); - s_instance = nullptr; + { + MutexLock lock(s_instance_mutex); + s_instance = nullptr; + } JNIEnv* env = GetJNIEnv(); env->CallVoidMethod(helper_, consent_info_helper::GetMethodId( @@ -227,7 +229,7 @@ void ConsentInfoInternalAndroid::JNI_ConsentInfoHelper_completeFuture( JNIEnv* env, jclass clazz, jint future_fn, jlong consent_info_internal_ptr, jlong future_handle, jint error_code, jobject error_message_obj) { MutexLock lock(s_instance_mutex); - if (consent_info_internal_ptr == 0 || s_instance == 0) { + if (consent_info_internal_ptr == 0 || s_instance == nullptr) { // Calling this with a null pointer, or if there is no active // instance, is a no-op, so just return. return; diff --git a/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java index 4c95a97b6b..4b2bf1852b 100644 --- a/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java +++ b/gma/src_java/com/google/firebase/gma/internal/cpp/ConsentInfoHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From b15029bbc52b5a16e8ddc0326df6d892760a7780 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Tue, 12 Sep 2023 13:20:34 -0700 Subject: [PATCH 54/56] Move lock to later in the destructor. --- gma/src/android/ump/consent_info_internal_android.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gma/src/android/ump/consent_info_internal_android.cc b/gma/src/android/ump/consent_info_internal_android.cc index a8c1844282..903b7126cd 100644 --- a/gma/src/android/ump/consent_info_internal_android.cc +++ b/gma/src/android/ump/consent_info_internal_android.cc @@ -154,14 +154,13 @@ static void ReleaseClasses(JNIEnv* env) { } ConsentInfoInternalAndroid::~ConsentInfoInternalAndroid() { - { - MutexLock lock(s_instance_mutex); - s_instance = nullptr; - } - JNIEnv* env = GetJNIEnv(); env->CallVoidMethod(helper_, consent_info_helper::GetMethodId( consent_info_helper::kDisconnect)); + + MutexLock lock(s_instance_mutex); + s_instance = nullptr; + env->DeleteGlobalRef(helper_); helper_ = nullptr; From 21fa4cd3a2047de48d1d3ef85404c37c34175655 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Tue, 12 Sep 2023 13:48:55 -0700 Subject: [PATCH 55/56] Format code. --- gma/integration_test/src/integration_test.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gma/integration_test/src/integration_test.cc b/gma/integration_test/src/integration_test.cc index 04006b24a9..71de054af8 100644 --- a/gma/integration_test/src/integration_test.cc +++ b/gma/integration_test/src/integration_test.cc @@ -356,6 +356,8 @@ void FirebaseGmaPreInitializationTests::SetUpTestSuite() { // Test cases below. +#if 0 // jsimantov + TEST_F(FirebaseGmaMinimalTest, TestInitializeGmaWithoutFirebase) { // Don't initialize mediation in this test so that a later test can still // verify that mediation has not been initialized. @@ -2475,6 +2477,8 @@ TEST_F(FirebaseGmaTest, TestAdViewMultithreadDeletion) { #endif // #if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && // TARGET_OS_IPHONE) +#endif // 0 // jsimantov + class FirebaseGmaUmpTest : public FirebaseGmaTest { public: FirebaseGmaUmpTest() : consent_info_(nullptr) {} From 924981b958f15685c75bfdb6dee057b91cd67c85 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 13 Sep 2023 16:51:24 -0700 Subject: [PATCH 56/56] Fix non-UI tests. --- gma/integration_test/src/integration_test.cc | 23 +++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/gma/integration_test/src/integration_test.cc b/gma/integration_test/src/integration_test.cc index 71de054af8..b51a96d3af 100644 --- a/gma/integration_test/src/integration_test.cc +++ b/gma/integration_test/src/integration_test.cc @@ -356,8 +356,6 @@ void FirebaseGmaPreInitializationTests::SetUpTestSuite() { // Test cases below. -#if 0 // jsimantov - TEST_F(FirebaseGmaMinimalTest, TestInitializeGmaWithoutFirebase) { // Don't initialize mediation in this test so that a later test can still // verify that mediation has not been initialized. @@ -2477,8 +2475,6 @@ TEST_F(FirebaseGmaTest, TestAdViewMultithreadDeletion) { #endif // #if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && // TARGET_OS_IPHONE) -#endif // 0 // jsimantov - class FirebaseGmaUmpTest : public FirebaseGmaTest { public: FirebaseGmaUmpTest() : consent_info_(nullptr) {} @@ -2962,7 +2958,8 @@ TEST_F(FirebaseGmaUmpTest, TestUmpMethodsReturnOperationInProgress) { ConsentRequestParameters params; params.tag_for_under_age_of_consent = false; params.debug_settings.debug_geography = - firebase::gma::ump::kConsentDebugGeographyEEA; + ShouldRunUITests() ? firebase::gma::ump::kConsentDebugGeographyEEA + : firebase::gma::ump::kConsentDebugGeographyNonEEA; params.debug_settings.debug_device_ids = kTestDeviceIDs; params.debug_settings.debug_device_ids.push_back(GetDebugDeviceId()); @@ -2977,17 +2974,17 @@ TEST_F(FirebaseGmaUmpTest, TestUmpMethodsReturnOperationInProgress) { WaitForCompletion(future_request_1, "RequestConsentInfoUpdate first"); FLAKY_TEST_SECTION_END(); - FLAKY_TEST_SECTION_BEGIN(); - firebase::Future future_load_1 = consent_info_->LoadConsentForm(); - firebase::Future future_load_2 = consent_info_->LoadConsentForm(); - WaitForCompletion(future_load_2, "LoadConsentForm second", - firebase::gma::ump::kConsentFormErrorOperationInProgress); - WaitForCompletion(future_load_1, "LoadConsentForm first"); - FLAKY_TEST_SECTION_END(); - if (ShouldRunUITests()) { // The below should only be checked if UI tests are enabled, as they // require some interaction. + FLAKY_TEST_SECTION_BEGIN(); + firebase::Future future_load_1 = consent_info_->LoadConsentForm(); + firebase::Future future_load_2 = consent_info_->LoadConsentForm(); + WaitForCompletion(future_load_2, "LoadConsentForm second", + firebase::gma::ump::kConsentFormErrorOperationInProgress); + WaitForCompletion(future_load_1, "LoadConsentForm first"); + FLAKY_TEST_SECTION_END(); + FLAKY_TEST_SECTION_BEGIN(); firebase::Future future_show_1 = consent_info_->ShowConsentForm(app_framework::GetWindowController());