Skip to content

Commit 92eb594

Browse files
authored
Add support for User Messaging Platform SDK on Android (#1428)
* Added debug device ID fetch to Android. * Add Java method for obtaining Android ID from secure settings. * Update android ID algorithm. * Add flatbuffers patch when building for Android. * Add stub Android files. * Fix path. * Add ConsentInfoHelper class with some starting functionality. * Fix GetDebugDeviceId on Android. * Format code. * Add UMP dependency. * Fix overrides. * Add initialization. * Lint. * Lint. * Move UMP resources to their own resource file and class path. * Fix relocated UMP resources. Add lint skip (like App Check). * Add enum conversion and fix build. * Add enum cache. * Android implementation of ConsentInfo functionality. * Format code. * Fix JNI type. * Fix Java build errors and format code. * Fix call. * Fix initialization. * Fix for debugDeviceId * Remove extra Enum method, and also format code. * Fix error message handling for Unavailable error. And format code. * Fix compile issue. * Remove extra debugging output. * Remove test filter for UMP only. * Remove extraneous HasExceptionOccurred function since ExceptionCheck does it for us. * 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. * Remove lint abortOnError false * Try enabling parallel gradle builds for Windows. * Restore script. * Remove lint on error abort. * Remove extra manifest file. * Restore build.gradle for GMA library proper * Temporarily remove ump_resources to see if this fixes Windows build hang. * Remove Flatbuffers patch to diagnose Windows hang. * Fix build error. * Add missing dependencies. * Revert "Add missing dependencies." This reverts commit 03decca. * Add ump-resources back in. * Add code back in. * Modify comment. * Remove lint abort. * Add compatibility version. * Move ConsentInfoHelper class into regular gma_resources. No need for a separate ump_resources. * Remove comment about build hang because spoiler alert, it doesn't cause it. * Ensure that GMA and UMP only load their shared class files once. * Rename mutex. * Update copyright date and fix lock issue. Also disable lint warning in this testapp (temporarily?) * Move lock to later in the destructor. * Format code. * Fix non-UI tests.
1 parent d704337 commit 92eb594

File tree

18 files changed

+1403
-17
lines changed

18 files changed

+1403
-17
lines changed

Android/firebase_dependencies.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def firebaseDependenciesMap = [
2727
'dynamic_links' : ['com.google.firebase:firebase-dynamic-links'],
2828
'firestore' : ['com.google.firebase:firebase-firestore'],
2929
'functions' : ['com.google.firebase:firebase-functions'],
30-
'gma' : ['com.google.android.gms:play-services-ads:22.3.0'],
30+
'gma' : ['com.google.android.gms:play-services-ads:22.3.0',
31+
'com.google.android.ump:user-messaging-platform:2.1.0'],
3132
'installations' : ['com.google.firebase:firebase-installations'],
3233
'invites' : ['com.google.firebase:firebase-invites'],
3334
// Messaging has an additional local dependency to include.

gma/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ binary_to_array("gma_resources"
4040
# Source files used by the Android implementation.
4141
set(android_SRCS
4242
${gma_resources_source}
43-
src/stub/ump/consent_info_internal_stub.cc
43+
src/android/ump/consent_info_internal_android.cc
4444
src/android/ad_request_converter.cc
4545
src/android/ad_error_android.cc
4646
src/android/adapter_response_info_android.cc

gma/gma_resources/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ allprojects {
3232
apply plugin: 'com.android.library'
3333

3434
android {
35+
compileOptions {
36+
sourceCompatibility 1.8
37+
targetCompatibility 1.8
38+
}
39+
3540
compileSdkVersion 28
3641

3742
sourceSets {
@@ -48,6 +53,7 @@ dependencies {
4853
implementation platform('com.google.firebase:firebase-bom:32.2.3')
4954
implementation 'com.google.firebase:firebase-analytics'
5055
implementation 'com.google.android.gms:play-services-ads:22.3.0'
56+
implementation 'com.google.android.ump:user-messaging-platform:2.1.0'
5157
}
5258

5359
afterEvaluate {

gma/integration_test/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ android {
7373
proguardFile file('proguard.pro')
7474
}
7575
}
76+
lintOptions {
77+
abortOnError false
78+
}
7679
}
7780

7881
apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"

gma/integration_test/src/integration_test.cc

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ void FirebaseGmaTest::SetUp() {
268268
// debugging. They appear as a long string of hex characters.
269269
firebase::gma::RequestConfiguration request_configuration;
270270
request_configuration.test_device_ids = kTestDeviceIDs;
271+
request_configuration.test_device_ids.push_back(GetDebugDeviceId());
271272
firebase::gma::SetRequestConfiguration(request_configuration);
272273
}
273274

@@ -332,6 +333,7 @@ void FirebaseGmaUITest::SetUp() {
332333
// debugging. They appear as a long string of hex characters.
333334
firebase::gma::RequestConfiguration request_configuration;
334335
request_configuration.test_device_ids = kTestDeviceIDs;
336+
request_configuration.test_device_ids.push_back(GetDebugDeviceId());
335337
firebase::gma::SetRequestConfiguration(request_configuration);
336338
}
337339

@@ -2493,7 +2495,12 @@ class FirebaseGmaUmpTest : public FirebaseGmaTest {
24932495

24942496
void FirebaseGmaUmpTest::InitializeUmp(ResetOption reset) {
24952497
using firebase::gma::ump::ConsentInfo;
2496-
consent_info_ = ConsentInfo::GetInstance(*shared_app_);
2498+
firebase::InitResult result;
2499+
consent_info_ = ConsentInfo::GetInstance(*shared_app_, &result);
2500+
2501+
EXPECT_NE(consent_info_, nullptr);
2502+
EXPECT_EQ(result, firebase::kInitResultSuccess);
2503+
24972504
if (consent_info_ != nullptr && reset == kReset) {
24982505
consent_info_->Reset();
24992506
}
@@ -2937,4 +2944,90 @@ TEST_F(FirebaseGmaUmpTest, TestUmpCleanupRaceCondition) {
29372944
EXPECT_EQ(future_show.status(), firebase::kFutureStatusInvalid);
29382945
}
29392946

2947+
TEST_F(FirebaseGmaUmpTest, TestUmpMethodsReturnOperationInProgress) {
2948+
SKIP_TEST_ON_DESKTOP;
2949+
2950+
using firebase::gma::ump::ConsentFormStatus;
2951+
using firebase::gma::ump::ConsentRequestParameters;
2952+
using firebase::gma::ump::ConsentStatus;
2953+
2954+
// Check that all of the UMP operations properly return an OperationInProgress
2955+
// error if called more than once at the same time. Each step of this test is
2956+
// inherently flaky, so add flaky test blocks all over.
2957+
2958+
ConsentRequestParameters params;
2959+
params.tag_for_under_age_of_consent = false;
2960+
params.debug_settings.debug_geography =
2961+
ShouldRunUITests() ? firebase::gma::ump::kConsentDebugGeographyEEA
2962+
: firebase::gma::ump::kConsentDebugGeographyNonEEA;
2963+
params.debug_settings.debug_device_ids = kTestDeviceIDs;
2964+
params.debug_settings.debug_device_ids.push_back(GetDebugDeviceId());
2965+
2966+
FLAKY_TEST_SECTION_BEGIN();
2967+
firebase::Future<void> future_request_1 =
2968+
consent_info_->RequestConsentInfoUpdate(params);
2969+
firebase::Future<void> future_request_2 =
2970+
consent_info_->RequestConsentInfoUpdate(params);
2971+
WaitForCompletion(
2972+
future_request_2, "RequestConsentInfoUpdate second",
2973+
firebase::gma::ump::kConsentRequestErrorOperationInProgress);
2974+
WaitForCompletion(future_request_1, "RequestConsentInfoUpdate first");
2975+
FLAKY_TEST_SECTION_END();
2976+
2977+
if (ShouldRunUITests()) {
2978+
// The below should only be checked if UI tests are enabled, as they
2979+
// require some interaction.
2980+
FLAKY_TEST_SECTION_BEGIN();
2981+
firebase::Future<void> future_load_1 = consent_info_->LoadConsentForm();
2982+
firebase::Future<void> future_load_2 = consent_info_->LoadConsentForm();
2983+
WaitForCompletion(future_load_2, "LoadConsentForm second",
2984+
firebase::gma::ump::kConsentFormErrorOperationInProgress);
2985+
WaitForCompletion(future_load_1, "LoadConsentForm first");
2986+
FLAKY_TEST_SECTION_END();
2987+
2988+
FLAKY_TEST_SECTION_BEGIN();
2989+
firebase::Future<void> future_show_1 =
2990+
consent_info_->ShowConsentForm(app_framework::GetWindowController());
2991+
firebase::Future<void> future_show_2 =
2992+
consent_info_->ShowConsentForm(app_framework::GetWindowController());
2993+
WaitForCompletion(future_show_2, "ShowConsentForm second",
2994+
firebase::gma::ump::kConsentFormErrorOperationInProgress);
2995+
WaitForCompletion(future_show_1, "ShowConsentForm first");
2996+
FLAKY_TEST_SECTION_END();
2997+
2998+
FLAKY_TEST_SECTION_BEGIN();
2999+
firebase::Future<void> future_privacy_1 =
3000+
consent_info_->ShowPrivacyOptionsForm(
3001+
app_framework::GetWindowController());
3002+
firebase::Future<void> future_privacy_2 =
3003+
consent_info_->ShowPrivacyOptionsForm(
3004+
app_framework::GetWindowController());
3005+
WaitForCompletion(future_privacy_2, "ShowPrivacyOptionsForm second",
3006+
firebase::gma::ump::kConsentFormErrorOperationInProgress);
3007+
WaitForCompletion(future_privacy_1, "ShowPrivacyOptionsForm first");
3008+
FLAKY_TEST_SECTION_END();
3009+
3010+
consent_info_->Reset();
3011+
// Request again so we can test LoadAndShowConsentFormIfRequired.
3012+
WaitForCompletion(consent_info_->RequestConsentInfoUpdate(params),
3013+
"RequestConsentInfoUpdate");
3014+
3015+
FLAKY_TEST_SECTION_BEGIN();
3016+
firebase::Future<void> future_load_and_show_1 =
3017+
consent_info_->LoadAndShowConsentFormIfRequired(
3018+
app_framework::GetWindowController());
3019+
firebase::Future<void> future_load_and_show_2 =
3020+
consent_info_->LoadAndShowConsentFormIfRequired(
3021+
app_framework::GetWindowController());
3022+
WaitForCompletion(future_load_and_show_2,
3023+
"LoadAndShowConsentFormIfRequired second",
3024+
firebase::gma::ump::kConsentFormErrorOperationInProgress);
3025+
WaitForCompletion(future_load_and_show_1,
3026+
"LoadAndShowConsentFormIfRequired first");
3027+
FLAKY_TEST_SECTION_END();
3028+
} else {
3029+
LogInfo("Skipping methods that require user interaction.");
3030+
}
3031+
}
3032+
29403033
} // namespace firebase_testapp_automated

gma/src/android/gma_android.cc

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
namespace firebase {
5050
namespace gma {
5151

52+
namespace internal {
53+
::firebase::Mutex g_cached_gma_embedded_files_mutex;
54+
std::vector<::firebase::internal::EmbeddedFile>* g_cached_gma_embedded_files =
55+
nullptr;
56+
} // namespace internal
57+
5258
METHOD_LOOKUP_DEFINITION(mobile_ads,
5359
PROGUARD_KEEP_CLASS
5460
"com/google/android/gms/ads/MobileAds",
@@ -308,12 +314,22 @@ Future<AdapterInitializationStatus> Initialize(JNIEnv* env, jobject activity,
308314
return Future<AdapterInitializationStatus>();
309315
}
310316

311-
const std::vector<firebase::internal::EmbeddedFile> embedded_files =
312-
util::CacheEmbeddedFiles(env, activity,
313-
firebase::internal::EmbeddedFile::ToVector(
314-
firebase_gma::gma_resources_filename,
315-
firebase_gma::gma_resources_data,
316-
firebase_gma::gma_resources_size));
317+
// Between this and UMP, we only want to load these files once.
318+
{
319+
MutexLock lock(internal::g_cached_gma_embedded_files_mutex);
320+
if (internal::g_cached_gma_embedded_files == nullptr) {
321+
internal::g_cached_gma_embedded_files =
322+
new std::vector<firebase::internal::EmbeddedFile>();
323+
*internal::g_cached_gma_embedded_files =
324+
util::CacheEmbeddedFiles(env, activity,
325+
firebase::internal::EmbeddedFile::ToVector(
326+
firebase_gma::gma_resources_filename,
327+
firebase_gma::gma_resources_data,
328+
firebase_gma::gma_resources_size));
329+
}
330+
}
331+
const std::vector<firebase::internal::EmbeddedFile>& embedded_files =
332+
*internal::g_cached_gma_embedded_files;
317333

318334
if (!(mobile_ads::CacheMethodIds(env, activity) &&
319335
ad_request_builder::CacheMethodIds(env, activity) &&

gma/src/android/gma_android.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919

2020
#include <jni.h>
2121

22+
#include <vector>
23+
24+
#include "app/src/embedded_file.h"
2225
#include "app/src/util_android.h"
26+
#include "firebase/internal/mutex.h"
2327
#include "gma/src/common/gma_common.h"
2428

2529
namespace firebase {
@@ -189,6 +193,14 @@ void ReleaseClasses(JNIEnv* env);
189193
jobject CreateJavaAdSize(JNIEnv* env, jobject activity,
190194
const AdSize& an_ad_size);
191195

196+
namespace internal {
197+
// GMA and UMP share embedded dex files; this ensures
198+
// that they are only loaded once each run.
199+
extern ::firebase::Mutex g_cached_gma_embedded_files_mutex;
200+
extern std::vector<::firebase::internal::EmbeddedFile>*
201+
g_cached_gma_embedded_files;
202+
} // namespace internal
203+
192204
} // namespace gma
193205
} // namespace firebase
194206

0 commit comments

Comments
 (0)