Skip to content

Commit c28b251

Browse files
authored
[Remote Config] Add Custom Signals (#1899)
* [Remote Config] Add Custom Signals * Real desktop support * Formatting * Update remote_config.h * Update readme.md * Update rest_test.cc * Address feedback * Update readme.md * More fixes * Pass config by reference * Update readme.md * Change rest call to be string to string * Fix windows issues * Formatting fix * Update rest_test.cc * Update remote_config_desktop.cc
1 parent 33ab7a4 commit c28b251

24 files changed

Lines changed: 744 additions & 46 deletions

release_build_files/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ code.
619619
- Auth (Desktop): Fixed log spam and high CPU utilization when offline by moving `USE_AUTH_EMULATOR` environment variable check to initialization and eliminating per-request logging (#1629).
620620
- Messaging: Added new Registration methods using Installation Ids.
621621
Deprecated old Token based methods.
622+
- Remote Config: Add support for setting Custom Signals.
622623

623624
### 13.11.0
624625
- Changes

remote_config/integration_test/src/integration_test.cc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,8 @@ TEST_F(FirebaseRemoteConfigTest, TestAddOnConfigUpdateListener) {
273273
// Check if the config has default values. If not, we have cached data
274274
// from a previous test run, and auto-fetch will not happen.
275275
EXPECT_TRUE(WaitForCompletion(SetDefaults(rc_), "SetDefaults"));
276-
bool validated_defaults = true;
277276
firebase::remote_config::ValueInfo value_info;
278-
bool bool_value = rc_->GetBoolean("TestBoolean", &value_info);
277+
rc_->GetBoolean("TestBoolean", &value_info);
279278
bool has_cached_data =
280279
value_info.source != firebase::remote_config::kValueSourceDefaultValue;
281280

@@ -507,4 +506,17 @@ TEST_F(FirebaseRemoteConfigTest, TestFetchSecondsParameter) {
507506

508507
FLAKY_TEST_SECTION_END();
509508
}
509+
510+
TEST_F(FirebaseRemoteConfigTest, TestSetCustomSignals) {
511+
ASSERT_NE(rc_, nullptr);
512+
513+
std::map<std::string, firebase::Variant> custom_signals = {
514+
{"test_string", firebase::Variant("alpha")},
515+
{"test_int", firebase::Variant(42)},
516+
{"test_double", firebase::Variant(3.14)}};
517+
518+
EXPECT_TRUE(WaitForCompletion(rc_->SetCustomSignals(custom_signals),
519+
"SetCustomSignals"));
520+
EXPECT_EQ(rc_->SetCustomSignalsLastResult().error(), 0);
521+
}
510522
} // namespace firebase_testapp_automated

remote_config/src/android/remote_config_android.cc

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ DEFINE_FIREBASE_VERSION_STRING(FirebaseRemoteConfig);
6060
X(SetConfigSettingsAsync, "setConfigSettingsAsync", \
6161
"(Lcom/google/firebase/remoteconfig/FirebaseRemoteConfigSettings;)" \
6262
"Lcom/google/android/gms/tasks/Task;"), \
63+
X(SetCustomSignalsAsync, "setCustomSignals", \
64+
"(Lcom/google/firebase/remoteconfig/CustomSignals;)" \
65+
"Lcom/google/android/gms/tasks/Task;"), \
6366
X(GetLong, "getLong", "(Ljava/lang/String;)J"), \
6467
X(GetString, "getString", "(Ljava/lang/String;)Ljava/lang/String;"), \
6568
X(GetBoolean, "getBoolean", "(Ljava/lang/String;)Z"), \
@@ -150,6 +153,30 @@ METHOD_LOOKUP_DEFINITION(
150153
"com/google/firebase/remoteconfig/FirebaseRemoteConfigSettings$Builder",
151154
REMOTE_CONFIG_SETTINGS_BUILDER_METHODS)
152155

156+
// Methods of CustomSignals.Builder
157+
// clang-format off
158+
#define CUSTOM_SIGNALS_BUILDER_METHODS(X) \
159+
X(Constructor, "<init>", "()V"), \
160+
X(PutString, "put", \
161+
"(Ljava/lang/String;Ljava/lang/String;)" \
162+
"Lcom/google/firebase/remoteconfig/CustomSignals$Builder;"), \
163+
X(PutLong, "put", \
164+
"(Ljava/lang/String;J)" \
165+
"Lcom/google/firebase/remoteconfig/CustomSignals$Builder;"), \
166+
X(PutDouble, "put", \
167+
"(Ljava/lang/String;D)" \
168+
"Lcom/google/firebase/remoteconfig/CustomSignals$Builder;"), \
169+
X(Build, "build", \
170+
"()Lcom/google/firebase/remoteconfig/CustomSignals;")
171+
// clang-format on
172+
METHOD_LOOKUP_DECLARATION(custom_signals_builder,
173+
CUSTOM_SIGNALS_BUILDER_METHODS)
174+
METHOD_LOOKUP_DEFINITION(
175+
custom_signals_builder,
176+
PROGUARD_KEEP_CLASS
177+
"com/google/firebase/remoteconfig/CustomSignals$Builder",
178+
CUSTOM_SIGNALS_BUILDER_METHODS)
179+
153180
// Methods of FirebaseRemoteConfigFetchThrottledException.
154181
// clang-format off
155182
#define REMOTE_CONFIG_THROTTLED_EXCEPTION_METHODS(X) \
@@ -248,6 +275,7 @@ static bool CacheJNIMethodIds(
248275
config_info::CacheMethodIds(env, activity) &&
249276
config_settings::CacheMethodIds(env, activity) &&
250277
config_settings_builder::CacheMethodIds(env, activity) &&
278+
custom_signals_builder::CacheMethodIds(env, activity) &&
251279
throttled_exception::CacheMethodIds(env, activity) &&
252280
config_update::CacheMethodIds(env, activity) &&
253281
config_update_listener_registration::CacheMethodIds(env, activity));
@@ -260,6 +288,7 @@ static void ReleaseClasses(JNIEnv* env) {
260288
config_info::ReleaseClass(env);
261289
config_settings::ReleaseClass(env);
262290
config_settings_builder::ReleaseClass(env);
291+
custom_signals_builder::ReleaseClass(env);
263292
throttled_exception::ReleaseClass(env);
264293
config_update::ReleaseClass(env);
265294
config_update_listener_registration::ReleaseClass(env);
@@ -361,6 +390,88 @@ static jobject ConfigKeyValueVariantArrayToHashMap(
361390
return hash_map;
362391
}
363392

393+
// Convert a std::map<std::string, Variant> into a Java CustomSignals object
394+
// using CustomSignals.Builder to populate String, Long, and Double signal
395+
// entries, or null values to clear signals.
396+
static jobject CustomSignalsFromMap(JNIEnv* env,
397+
const std::map<std::string, Variant>& map) {
398+
jobject builder = env->NewObject(custom_signals_builder::GetClass(),
399+
custom_signals_builder::GetMethodId(
400+
custom_signals_builder::kConstructor));
401+
if (util::CheckAndClearJniExceptions(env) || !builder) return nullptr;
402+
403+
for (const auto& kv : map) {
404+
jstring key = env->NewStringUTF(kv.first.c_str());
405+
if (util::CheckAndClearJniExceptions(env) || !key) {
406+
env->DeleteLocalRef(builder);
407+
return nullptr;
408+
}
409+
jobject result_builder = nullptr;
410+
if (kv.second.is_null()) {
411+
result_builder =
412+
env->CallObjectMethod(builder,
413+
custom_signals_builder::GetMethodId(
414+
custom_signals_builder::kPutString),
415+
key, nullptr);
416+
} else if (kv.second.is_string()) {
417+
jstring str_val = env->NewStringUTF(kv.second.string_value());
418+
if (util::CheckAndClearJniExceptions(env) || !str_val) {
419+
env->DeleteLocalRef(key);
420+
env->DeleteLocalRef(builder);
421+
return nullptr;
422+
}
423+
result_builder =
424+
env->CallObjectMethod(builder,
425+
custom_signals_builder::GetMethodId(
426+
custom_signals_builder::kPutString),
427+
key, str_val);
428+
env->DeleteLocalRef(str_val);
429+
} else if (kv.second.is_int64()) {
430+
result_builder = env->CallObjectMethod(
431+
builder,
432+
custom_signals_builder::GetMethodId(custom_signals_builder::kPutLong),
433+
key, kv.second.int64_value());
434+
} else if (kv.second.is_double()) {
435+
result_builder =
436+
env->CallObjectMethod(builder,
437+
custom_signals_builder::GetMethodId(
438+
custom_signals_builder::kPutDouble),
439+
key, kv.second.double_value());
440+
} else {
441+
LogError(
442+
"Remote Config: Invalid Variant type for SetCustomSignals() key %s.",
443+
kv.first.c_str());
444+
env->DeleteLocalRef(key);
445+
env->DeleteLocalRef(builder);
446+
return nullptr;
447+
}
448+
449+
if (util::CheckAndClearJniExceptions(env) || !result_builder) {
450+
if (result_builder) {
451+
env->DeleteLocalRef(result_builder);
452+
}
453+
env->DeleteLocalRef(key);
454+
env->DeleteLocalRef(builder);
455+
return nullptr;
456+
}
457+
458+
env->DeleteLocalRef(key);
459+
env->DeleteLocalRef(result_builder);
460+
}
461+
462+
jobject custom_signals = env->CallObjectMethod(
463+
builder,
464+
custom_signals_builder::GetMethodId(custom_signals_builder::kBuild));
465+
if (util::CheckAndClearJniExceptions(env) || !custom_signals) {
466+
if (custom_signals) {
467+
env->DeleteLocalRef(custom_signals);
468+
}
469+
custom_signals = nullptr;
470+
}
471+
env->DeleteLocalRef(builder);
472+
return custom_signals;
473+
}
474+
364475
// Check pending exceptions following a key fetch and log an error if a
365476
// failure occurred. If an error occurs this method returns true, false
366477
// otherwise.
@@ -959,6 +1070,40 @@ Future<void> RemoteConfigInternal::SetConfigSettingsLastResult() {
9591070
future_impl_.LastResult(kRemoteConfigFnSetConfigSettings));
9601071
}
9611072

1073+
Future<void> RemoteConfigInternal::SetCustomSignals(
1074+
const std::map<std::string, Variant>& custom_signals) {
1075+
const auto handle =
1076+
future_impl_.SafeAlloc<void>(kRemoteConfigFnSetCustomSignals);
1077+
JNIEnv* env = app_.GetJNIEnv();
1078+
jobject j_custom_signals = CustomSignalsFromMap(env, custom_signals);
1079+
if (!j_custom_signals) {
1080+
future_impl_.Complete(handle, kFutureStatusFailure,
1081+
"SetCustomSignals native function fails");
1082+
return MakeFuture<void>(&future_impl_, handle);
1083+
}
1084+
jobject task = env->CallObjectMethod(
1085+
internal_obj_, config::GetMethodId(config::kSetCustomSignalsAsync),
1086+
j_custom_signals);
1087+
if (util::CheckAndClearJniExceptions(env)) {
1088+
task = nullptr;
1089+
future_impl_.Complete(handle, kFutureStatusFailure,
1090+
"SetCustomSignals native function fails");
1091+
} else {
1092+
auto data_handle = new RCDataHandle<void>(&future_impl_, handle, this);
1093+
util::RegisterCallbackOnTask(env, task, CompleteVoidCallback,
1094+
reinterpret_cast<void*>(data_handle),
1095+
jni_task_id_.c_str());
1096+
}
1097+
env->DeleteLocalRef(task);
1098+
env->DeleteLocalRef(j_custom_signals);
1099+
return MakeFuture<void>(&future_impl_, handle);
1100+
}
1101+
1102+
Future<void> RemoteConfigInternal::SetCustomSignalsLastResult() {
1103+
return static_cast<const Future<void>&>(
1104+
future_impl_.LastResult(kRemoteConfigFnSetCustomSignals));
1105+
}
1106+
9621107
ConfigSettings RemoteConfigInternal::GetConfigSettings() {
9631108
ConfigSettings settings;
9641109
JNIEnv* env = app_.GetJNIEnv();

remote_config/src/android/remote_config_android.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class RemoteConfigInternal {
6666
Future<void> SetDefaultsLastResult();
6767
Future<void> SetConfigSettings(ConfigSettings settings);
6868
Future<void> SetConfigSettingsLastResult();
69+
Future<void> SetCustomSignals(
70+
const std::map<std::string, Variant>& custom_signals);
71+
Future<void> SetCustomSignalsLastResult();
6972
ConfigSettings GetConfigSettings();
7073
bool GetBoolean(const char* key, ValueInfo* info);
7174

remote_config/src/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ enum RemoteConfigFn {
2828
kRemoteConfigFnFetchAndActivate,
2929
kRemoteConfigFnSetDefaults,
3030
kRemoteConfigFnSetConfigSettings,
31+
kRemoteConfigFnSetCustomSignals,
3132
kRemoteConfigFnCount
3233
};
3334

remote_config/src/desktop/metadata.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <limits>
2121
#include <map>
2222
#include <string>
23+
#include <vector>
2324

2425
#include "flatbuffers/flexbuffers.h"
2526
#include "remote_config/src/include/firebase/remote_config.h"
@@ -53,6 +54,20 @@ std::string RemoteConfigMetadata::Serialize() const {
5354
fbb.String(std::to_string(setting.first).c_str(), setting.second);
5455
}
5556
});
57+
58+
fbb.Map("custom_signals", [&]() {
59+
for (const auto& signal : custom_signals_) {
60+
const std::string& key = signal.first;
61+
const Variant& val = signal.second;
62+
if (val.is_string()) {
63+
fbb.String(key.c_str(), val.string_value());
64+
} else if (val.is_int64()) {
65+
fbb.Int(key.c_str(), val.int64_value());
66+
} else if (val.is_double()) {
67+
fbb.Double(key.c_str(), val.double_value());
68+
}
69+
}
70+
});
5671
});
5772
fbb.Finish();
5873
const std::vector<uint8_t>& buffer = fbb.GetBuffer();
@@ -98,6 +113,22 @@ void RemoteConfigMetadata::Deserialize(const std::string& buffer) {
98113
settings_[static_cast<ConfigSetting>(int_key)] =
99114
settings.Values()[i].AsString().c_str();
100115
}
116+
117+
custom_signals_.clear();
118+
flexbuffers::Map custom_signals = struct_map["custom_signals"].AsMap();
119+
for (size_t i = 0, n = custom_signals.size(); i < n; ++i) {
120+
const char* key_str = custom_signals.Keys()[i].AsKey();
121+
if (!key_str) continue;
122+
flexbuffers::Reference val_ref = custom_signals.Values()[i];
123+
if (val_ref.IsString()) {
124+
custom_signals_[key_str] =
125+
Variant(std::string(val_ref.AsString().c_str()));
126+
} else if (val_ref.IsInt()) {
127+
custom_signals_[key_str] = Variant(val_ref.AsInt64());
128+
} else if (val_ref.IsFloat()) {
129+
custom_signals_[key_str] = Variant(val_ref.AsDouble());
130+
}
131+
}
101132
}
102133

103134
void RemoteConfigMetadata::AddSetting(const ConfigSetting& setting,
@@ -116,6 +147,7 @@ std::string RemoteConfigMetadata::GetSetting(
116147
bool RemoteConfigMetadata::operator==(const RemoteConfigMetadata& right) const {
117148
return digest_by_namespace_ == right.digest_by_namespace_ &&
118149
settings_ == right.settings_ &&
150+
custom_signals_ == right.custom_signals_ &&
119151
info_.fetch_time == right.info_.fetch_time &&
120152
info_.last_fetch_status == right.info_.last_fetch_status &&
121153
info_.last_fetch_failure_reason ==

remote_config/src/desktop/metadata.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <map>
1919
#include <string>
2020

21+
#include "firebase/variant.h"
2122
#include "flatbuffers/flexbuffers.h"
2223
#include "remote_config/src/include/firebase/remote_config.h"
2324

@@ -27,6 +28,7 @@ namespace internal {
2728

2829
typedef std::map<std::string, std::string> MetaDigestMap;
2930
typedef std::map<ConfigSetting, std::string> MetaSettingsMap;
31+
typedef std::map<std::string, Variant> MetaCustomSignalsMap;
3032

3133
// Contains different data about Remote Config Client.
3234
//
@@ -38,6 +40,7 @@ typedef std::map<ConfigSetting, std::string> MetaSettingsMap;
3840
// * settings map: corresponds to a single supported setting, "developer mode"
3941
// * digest map: Server computed digest (hash) of the config entries, stored
4042
// per config namespace.
43+
// * custom_signals map: Custom signals dictionary set by the developer.
4144
class RemoteConfigMetadata {
4245
public:
4346
RemoteConfigMetadata();
@@ -62,6 +65,12 @@ class RemoteConfigMetadata {
6265
// Return setting value by setting. Return "0" if value does not given.
6366
std::string GetSetting(const ConfigSetting& setting) const;
6467

68+
// Returns a map of custom signals.
69+
const MetaCustomSignalsMap& custom_signals() const { return custom_signals_; }
70+
void set_custom_signals(const MetaCustomSignalsMap& custom_signals) {
71+
custom_signals_ = custom_signals;
72+
}
73+
6574
bool operator==(const RemoteConfigMetadata& right) const;
6675

6776
private:
@@ -80,6 +89,9 @@ class RemoteConfigMetadata {
8089
// For now it's only one key: kConfigSettingDeveloperMode. Set "1" to enable
8190
// and "0" to disable.
8291
MetaSettingsMap settings_;
92+
93+
// Custom signals.
94+
MetaCustomSignalsMap custom_signals_;
8395
};
8496

8597
// Helper to deserialize elements of a Flexbuffer Map to map or unordered map,

0 commit comments

Comments
 (0)