Skip to content

Clean up JNI task listeners per object #1358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 28, 2023
Merged
16 changes: 16 additions & 0 deletions app/src/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -342,5 +342,21 @@ std::vector<std::string> SplitString(const std::string& s,
return split_parts;
}

// Id used as part of the logic to make unique api identifiers.
static int g_api_identifier_count = 0;

std::string CreateApiIdentifier(const char* api_id, void* object) {
std::string created;
const char* format = "%s0x%016llx_%d";
unsigned long long object_ptr = // NOLINT
static_cast<unsigned long long>( // NOLINT
reinterpret_cast<intptr_t>(object));
int extra_id = g_api_identifier_count++;
int size = snprintf(nullptr, 0, format, api_id, object_ptr, extra_id) + 1;
created.reserve(size);
snprintf(&(created[0]), size, format, api_id, object_ptr, extra_id);
return created;
}

// NOLINTNEXTLINE - allow namespace overridden
} // namespace firebase
6 changes: 5 additions & 1 deletion app/src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,11 @@ class StaticFutureData {
// delimiter. Returns a vector of constituent parts.
std::vector<std::string> SplitString(const std::string& s, char delimiter);

// NOLINTNEXTLINE - allow namespace overridden
// Helper function to combine a string and other object into a unique
// identifier. Primarily used to manage listening to JNI Tasks on Android.
// Note that repeated calls are expected to return different values.
std::string CreateApiIdentifier(const char* api_id, void* object);

} // namespace firebase

#endif // FIREBASE_APP_SRC_UTIL_H_
12 changes: 12 additions & 0 deletions app/tests/util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,17 @@ TEST(UtilTest, SplitStringDelimetersOnly) {
EXPECT_THAT(SplitString("///", '/'), IsEmpty());
}

TEST(UtilTest, CreateApiIdentifierUnique) {
int v1, v2;
EXPECT_STRNE(CreateApiIdentifier("Test", &v1).c_str(),
CreateApiIdentifier("Test", &v2).c_str());
}

TEST(UtilTest, CreateApiIdentifierReallyUnique) {
int v1;
EXPECT_STRNE(CreateApiIdentifier("Test", &v1).c_str(),
CreateApiIdentifier("Test", &v1).c_str());
}

} // namespace
} // namespace firebase
10 changes: 6 additions & 4 deletions app_check/src/android/app_check_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "app/src/include/firebase/future.h"
#include "app/src/include/firebase/internal/mutex.h"
#include "app/src/reference_counted_future_impl.h"
#include "app/src/util.h"
#include "app/src/util_android.h"
#include "app_check/app_check_resources.h"
#include "app_check/src/android/common_android.h"
Expand Down Expand Up @@ -135,8 +136,6 @@ static const JNINativeMethod kNativeJniAppCheckListenerMethods[] = {
reinterpret_cast<void*>(JniAppCheckListener_nativeOnAppCheckTokenChanged)},
};

static const char* kApiIdentifier = "AppCheck";

static AppCheckProviderFactory* g_provider_factory = nullptr;
static int g_initialized_count = 0;

Expand Down Expand Up @@ -306,6 +305,9 @@ AppCheckInternal::AppCheckInternal(App* app) : app_(app) {
g_initialized_count++;
}

static const char* kApiIdentifier = "AppCheck";
jni_task_id_ = CreateApiIdentifier(kApiIdentifier, this);

// Create the FirebaseAppCheck class in Java.
jobject platform_app = app->GetPlatformApp();
jobject j_app_check_local = env->CallStaticObjectMethod(
Expand Down Expand Up @@ -364,6 +366,7 @@ AppCheckInternal::~AppCheckInternal() {
JNIEnv* env = app_->GetJNIEnv();
app_ = nullptr;
listeners_.clear();
util::CancelCallbacks(env, jni_task_id_.c_str());

if (j_app_check_listener_ != nullptr) {
env->CallVoidMethod(
Expand Down Expand Up @@ -393,7 +396,6 @@ AppCheckInternal::~AppCheckInternal() {
FIREBASE_ASSERT(g_initialized_count);
g_initialized_count--;
if (g_initialized_count == 0) {
util::CancelCallbacks(env, kApiIdentifier);
ReleaseClasses(env);
util::Terminate(env);
}
Expand Down Expand Up @@ -435,7 +437,7 @@ Future<AppCheckToken> AppCheckInternal::GetAppCheckToken(bool force_refresh) {
auto data_handle = new FutureDataHandle(future(), handle);
util::RegisterCallbackOnTask(env, j_task, TokenResultCallback,
reinterpret_cast<void*>(data_handle),
kApiIdentifier);
jni_task_id_.c_str());
} else {
AppCheckToken empty_token;
future()->CompleteWithResult(handle, kAppCheckErrorUnknown, error.c_str(),
Expand Down
4 changes: 4 additions & 0 deletions app_check/src/android/app_check_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef FIREBASE_APP_CHECK_SRC_ANDROID_APP_CHECK_ANDROID_H_
#define FIREBASE_APP_CHECK_SRC_ANDROID_APP_CHECK_ANDROID_H_

#include <string>
#include <vector>

#include "app/src/future_manager.h"
Expand Down Expand Up @@ -70,6 +71,9 @@ class AppCheckInternal {
Mutex listeners_mutex_;

FutureManager future_manager_;

// String to be used when registering for JNI task callbacks.
std::string jni_task_id_;
};

} // namespace internal
Expand Down
9 changes: 6 additions & 3 deletions app_check/src/android/common_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <string>

#include "app/src/app_common.h"
#include "app/src/util.h"
#include "app/src/util_android.h"
#include "app_check/src/include/firebase/app_check.h"

Expand Down Expand Up @@ -45,8 +46,6 @@ METHOD_LOOKUP_DEFINITION(app_check_token,
"com/google/firebase/appcheck/AppCheckToken",
APP_CHECK_TOKEN_METHODS)

static const char* kApiIdentifier = "AppCheckProvider";

bool CacheCommonAndroidMethodIds(JNIEnv* env, jobject activity) {
// Cache the token and provider classes.
if (!(app_check_token::CacheMethodIds(env, activity) &&
Expand Down Expand Up @@ -124,12 +123,15 @@ void TokenResultCallback(JNIEnv* env, jobject result,

AndroidAppCheckProvider::AndroidAppCheckProvider(jobject local_provider)
: android_provider_(nullptr) {
static const char* kApiIdentifier = "AppCheckProvider";
jni_task_id_ = CreateApiIdentifier(kApiIdentifier, this);
JNIEnv* env = GetJniEnv();
android_provider_ = env->NewGlobalRef(local_provider);
}

AndroidAppCheckProvider::~AndroidAppCheckProvider() {
JNIEnv* env = GetJniEnv();
util::CancelCallbacks(env, jni_task_id_.c_str());
if (env != nullptr && android_provider_ != nullptr) {
env->DeleteGlobalRef(android_provider_);
}
Expand All @@ -150,7 +152,8 @@ void AndroidAppCheckProvider::GetToken(
new TokenResultCallbackData(completion_callback);
util::RegisterCallbackOnTask(
env, j_task, TokenResultCallback,
reinterpret_cast<void*>(completion_callback_data), kApiIdentifier);
reinterpret_cast<void*>(completion_callback_data),
jni_task_id_.c_str());
} else {
AppCheckToken empty_token;
completion_callback(empty_token, kAppCheckErrorUnknown, error.c_str());
Expand Down
3 changes: 3 additions & 0 deletions app_check/src/android/common_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class AndroidAppCheckProvider : public AppCheckProvider {

private:
jobject android_provider_;

// String to be used when registering for JNI task callbacks.
std::string jni_task_id_;
};

} // namespace internal
Expand Down
9 changes: 1 addition & 8 deletions auth/src/auth.cc
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,8 @@ Auth::Auth(App* app, void* auth_impl) : auth_data_(new AuthData) {
auth_data_->user_impl = nullptr;
InitPlatformAuth(auth_data_);

std::string* future_id = &auth_data_->future_api_id;
static const char* kApiIdentifier = "Auth";
future_id->reserve(strlen(kApiIdentifier) +
16 /* hex characters in the pointer */ +
1 /* null terminator */);
snprintf(&((*future_id)[0]), future_id->capacity(), "%s0x%016llx",
kApiIdentifier,
static_cast<unsigned long long>( // NOLINT
reinterpret_cast<intptr_t>(this)));
auth_data_->future_api_id = CreateApiIdentifier(kApiIdentifier, this);

// Cleanup this object if app is destroyed.
CleanupNotifier* notifier = CleanupNotifier::FindByOwner(app);
Expand Down
4 changes: 4 additions & 0 deletions database/src/android/database_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "app/src/include/firebase/internal/common.h"
#include "app/src/include/firebase/log.h"
#include "app/src/reference_counted_future_impl.h"
#include "app/src/util.h"
#include "app/src/util_android.h"
#include "database/database_resources.h"
#include "database/src/android/data_snapshot_android.h"
Expand Down Expand Up @@ -172,6 +173,7 @@ DatabaseInternal::DatabaseInternal(App* app)
app_ = nullptr;
if (!Initialize(app)) return;
app_ = app;
jni_task_id_ = CreateApiIdentifier(kApiIdentifier, this);

JNIEnv* env = app_->GetJNIEnv();
jobject platform_app = app->GetPlatformApp();
Expand All @@ -198,6 +200,7 @@ DatabaseInternal::DatabaseInternal(App* app, const char* url)
app_ = nullptr;
if (!Initialize(app)) return;
app_ = app;
jni_task_id_ = CreateApiIdentifier(kApiIdentifier, this);

JNIEnv* env = app_->GetJNIEnv();
jobject url_string = env->NewStringUTF(url);
Expand Down Expand Up @@ -399,6 +402,7 @@ DatabaseInternal::~DatabaseInternal() {
cleanup_.CleanupAll();

JNIEnv* env = app_->GetJNIEnv();
util::CancelCallbacks(env, jni_task_id_.c_str());
{
// If there are any pending listeners, delete their pointers.
MutexLock lock(listener_mutex_);
Expand Down
12 changes: 8 additions & 4 deletions database/src/android/database_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <map>
#include <set>
#include <string>
#include <vector>

#include "app/src/cleanup_notifier.h"
Expand All @@ -42,10 +43,6 @@ namespace internal {
// For constructing, copying or moving DatabaseReferences atomically.
extern Mutex g_database_reference_constructor_mutex;

// Used for registering global callbacks. See
// firebase::util::RegisterCallbackOnTask for context.
extern const char kApiIdentifier[];

// This is the Android implementation of Database.
class DatabaseInternal {
public:
Expand Down Expand Up @@ -183,6 +180,10 @@ class DatabaseInternal {

Logger* logger() { return &logger_; }

// Used for registering global callbacks. See
// firebase::util::RegisterCallbackOnTask for context.
const char* jni_task_id() { return jni_task_id_.c_str(); }

private:
static bool Initialize(App* app);
static void ReleaseClasses(App* app);
Expand Down Expand Up @@ -222,6 +223,9 @@ class DatabaseInternal {
std::string constructor_url_;

Logger logger_;

// String to be used when registering for JNI task callbacks.
std::string jni_task_id_;
};

} // namespace internal
Expand Down
10 changes: 5 additions & 5 deletions database/src/android/database_reference_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ Future<void> DatabaseReferenceInternal::RemoveValue() {
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(
new FutureCallbackData(handle, ref_future(), db_)),
kApiIdentifier);
db_->jni_task_id());
util::CheckAndClearJniExceptions(env);
env->DeleteLocalRef(task);
return MakeFuture(ref_future(), handle);
Expand Down Expand Up @@ -358,7 +358,7 @@ Future<void> DatabaseReferenceInternal::SetValue(Variant value) {
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(
new FutureCallbackData(handle, ref_future(), db_)),
kApiIdentifier);
db_->jni_task_id());
env->DeleteLocalRef(task);
if (value_obj) env->DeleteLocalRef(value_obj);
}
Expand Down Expand Up @@ -391,7 +391,7 @@ Future<void> DatabaseReferenceInternal::SetPriority(Variant priority) {
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(
new FutureCallbackData(handle, ref_future(), db_)),
kApiIdentifier);
db_->jni_task_id());
util::CheckAndClearJniExceptions(env);
env->DeleteLocalRef(task);
if (priority_obj) env->DeleteLocalRef(priority_obj);
Expand Down Expand Up @@ -432,7 +432,7 @@ Future<void> DatabaseReferenceInternal::SetValueAndPriority(Variant value,
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(
new FutureCallbackData(handle, ref_future(), db_)),
kApiIdentifier);
db_->jni_task_id());
env->DeleteLocalRef(task);
if (value_obj) env->DeleteLocalRef(value_obj);
if (priority_obj) env->DeleteLocalRef(priority_obj);
Expand Down Expand Up @@ -464,7 +464,7 @@ Future<void> DatabaseReferenceInternal::UpdateChildren(Variant values) {
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(
new FutureCallbackData(handle, ref_future(), db_)),
kApiIdentifier);
db_->jni_task_id());
env->DeleteLocalRef(task);
if (values_obj) env->DeleteLocalRef(values_obj);
}
Expand Down
10 changes: 5 additions & 5 deletions database/src/android/disconnection_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Future<void> DisconnectionHandlerInternal::Cancel() {
env, task, FutureCallback,
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(new FutureCallbackData(handle, future(), db_)),
kApiIdentifier);
db_->jni_task_id());
util::CheckAndClearJniExceptions(env);
env->DeleteLocalRef(task);
return MakeFuture(future(), handle);
Expand All @@ -141,7 +141,7 @@ Future<void> DisconnectionHandlerInternal::RemoveValue() {
env, task, FutureCallback,
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(new FutureCallbackData(handle, future(), db_)),
kApiIdentifier);
db_->jni_task_id());
util::CheckAndClearJniExceptions(env);
return MakeFuture(future(), handle);
}
Expand All @@ -166,7 +166,7 @@ Future<void> DisconnectionHandlerInternal::SetValue(Variant value) {
env, task, FutureCallback,
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(new FutureCallbackData(handle, future(), db_)),
kApiIdentifier);
db_->jni_task_id());
util::CheckAndClearJniExceptions(env);
env->DeleteLocalRef(task);
if (value_obj) env->DeleteLocalRef(value_obj);
Expand Down Expand Up @@ -211,7 +211,7 @@ Future<void> DisconnectionHandlerInternal::SetValueAndPriority(
env, task, FutureCallback,
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(new FutureCallbackData(handle, future(), db_)),
kApiIdentifier);
db_->jni_task_id());
env->DeleteLocalRef(task);
if (value_obj) env->DeleteLocalRef(value_obj);
}
Expand Down Expand Up @@ -240,7 +240,7 @@ Future<void> DisconnectionHandlerInternal::UpdateChildren(Variant values) {
env, task, FutureCallback,
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(new FutureCallbackData(handle, future(), db_)),
kApiIdentifier);
db_->jni_task_id());
env->DeleteLocalRef(task);
if (values_obj) env->DeleteLocalRef(values_obj);
}
Expand Down
4 changes: 2 additions & 2 deletions functions/src/android/callable_reference_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Future<HttpsCallableResult> HttpsCallableReferenceInternal::Call() {
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(new FutureCallbackData(
handle, future(), functions_, kCallableReferenceFnCall)),
kApiIdentifier);
functions_->jni_task_id());

util::CheckAndClearJniExceptions(env);
env->DeleteLocalRef(task);
Expand Down Expand Up @@ -219,7 +219,7 @@ Future<HttpsCallableResult> HttpsCallableReferenceInternal::Call(
// FutureCallback will delete the newed FutureCallbackData.
reinterpret_cast<void*>(new FutureCallbackData(
handle, future(), functions_, kCallableReferenceFnCall)),
kApiIdentifier);
functions_->jni_task_id());

util::CheckAndClearJniExceptions(env);
env->DeleteLocalRef(task);
Expand Down
Loading