Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion desktopApp/src/main/c/SparkeBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ typedef void (*SparkleLogCallback)(SparkleLogLevel level, const char* operation,
* Called when Sparkle needs the application to shut down for update installation
*/
typedef void (*SparkleShutdownCallback)(void);
typedef void (*SparkleUpdateCallback)(const char* version, const char* description);
typedef void (*SparkleNoUpdateCallback)(void);

/**
* Set log callback for receiving log messages
Expand All @@ -40,6 +42,8 @@ void sparkle_set_log_callback(SparkleLogCallback callback);
* @param callback Function pointer to shutdown callback, or NULL to disable
*/
void sparkle_set_shutdown_callback(SparkleShutdownCallback callback);
void sparkle_set_update_callback(SparkleUpdateCallback callback);
void sparkle_set_no_update_callback(SparkleNoUpdateCallback callback);

/**
* Initialize Sparkle updater with appcast URL and optional public key
Expand Down Expand Up @@ -80,4 +84,4 @@ int sparkle_cleanup(void);
}
#endif

#endif // SPARKLE_BRIDGE_H
#endif // SPARKLE_BRIDGE_H
16 changes: 16 additions & 0 deletions desktopApp/src/main/c/SparkeBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
static SPUStandardUpdaterController* updaterController = nil;
static SparkleLogCallback logCallback = NULL;
static SparkleShutdownCallback shutdownCallback = NULL;
static SparkleUpdateCallback updateCallback = NULL;
static SparkleNoUpdateCallback noUpdateCallback = NULL;

// Internal logging function that handles both NSLog and callback
static void sparkle_log(SparkleLogLevel level, const char* operation, const char* format, ...) {
Expand Down Expand Up @@ -62,10 +64,16 @@ - (void)updater:(SPUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item {
sparkle_log(SPARKLE_LOG_INFO, "update_lifecycle", "Found valid update: %s (current: %s)",
[item.displayVersionString UTF8String],
[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String]);
if (updateCallback != NULL) {
updateCallback([item.displayVersionString UTF8String], "");
}
}

- (void)updaterDidNotFindUpdate:(SPUUpdater *)updater {
sparkle_log(SPARKLE_LOG_INFO, "update_lifecycle", "No update found - application is up to date");
if (noUpdateCallback != NULL) {
noUpdateCallback();
}
}

- (void)updater:(SPUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvoked:(BOOL)immediateInstallation {
Expand Down Expand Up @@ -98,6 +106,14 @@ void sparkle_set_shutdown_callback(SparkleShutdownCallback callback) {
sparkle_log(SPARKLE_LOG_INFO, "callback", "Shutdown callback %s", callback ? "enabled" : "disabled");
}

void sparkle_set_update_callback(SparkleUpdateCallback callback) {
updateCallback = callback;
}

void sparkle_set_no_update_callback(SparkleNoUpdateCallback callback) {
noUpdateCallback = callback;
}

int sparkle_init(const char* appcast_url, const char* public_key) {
__block int result = 0;

Expand Down
131 changes: 101 additions & 30 deletions desktopApp/src/main/c/UpdateBridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {

static const char* jstring_to_cstring(JNIEnv* env, jstring jstr);
static void release_cstring(JNIEnv* env, jstring jstr, const char* cstr);
static JNIEnv* get_callback_env(int* detach_when_done);

// Global JVM and callback references
static JavaVM* g_jvm = NULL;
static jobject g_logCallbackObject = NULL;
static jmethodID g_logCallbackMethod = NULL;
static jmethodID g_updateFoundCallbackMethod = NULL;
static jmethodID g_noUpdateCallbackMethod = NULL;
static jobject g_shutdownCallbackObject = NULL;
static jmethodID g_shutdownCallbackMethod = NULL;

Expand All @@ -53,6 +56,23 @@ static void release_cstring(JNIEnv* env, jstring jstr, const char* cstr) {
}
}

static JNIEnv* get_callback_env(int* detach_when_done) {
JNIEnv* env = NULL;
*detach_when_done = 0;

jint result = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
if (result == JNI_OK) {
return env;
}
if (result != JNI_EDETACHED ||
(*g_jvm)->AttachCurrentThread(g_jvm, (void**)&env, NULL) != JNI_OK) {
return NULL;
}

*detach_when_done = 1;
return env;
}

// Log callback function that forwards to Java
#ifdef __APPLE__
static void native_log_callback(SparkleLogLevel level, const char* operation, const char* message) {
Expand All @@ -63,10 +83,9 @@ static void native_log_callback(WinSparkleLogLevel level, const char* operation,
return;
}

JNIEnv* env = NULL;
if ((*g_jvm)->AttachCurrentThread(g_jvm, (void**)&env, NULL) != JNI_OK) {
return;
}
int detach_when_done;
JNIEnv* env = get_callback_env(&detach_when_done);
if (env == NULL) return;

// Create Java strings
jstring jOperation = (*env)->NewStringUTF(env, operation);
Expand All @@ -82,7 +101,7 @@ static void native_log_callback(WinSparkleLogLevel level, const char* operation,
if (jOperation != NULL) (*env)->DeleteLocalRef(env, jOperation);
if (jMessage != NULL) (*env)->DeleteLocalRef(env, jMessage);

(*g_jvm)->DetachCurrentThread(g_jvm);
if (detach_when_done) (*g_jvm)->DetachCurrentThread(g_jvm);
}

// Shutdown callback function that forwards to Java
Expand All @@ -91,71 +110,109 @@ static void native_shutdown_callback(void) {
return;
}

JNIEnv* env = NULL;
if ((*g_jvm)->AttachCurrentThread(g_jvm, (void**)&env, NULL) != JNI_OK) {
return;
}
int detach_when_done;
JNIEnv* env = get_callback_env(&detach_when_done);
if (env == NULL) return;

// Call Java shutdown callback method
(*env)->CallVoidMethod(env, g_shutdownCallbackObject, g_shutdownCallbackMethod);

(*g_jvm)->DetachCurrentThread(g_jvm);
if (detach_when_done) (*g_jvm)->DetachCurrentThread(g_jvm);
}

// JNI function to set log callback
JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_UpdateManagerBase_nativeSetLogCallback(JNIEnv* env, jobject obj, jobject callback) {
// Get JavaVM for later use
if (g_jvm == NULL) {
if ((*env)->GetJavaVM(env, &g_jvm) != JNI_OK) {
return -1;
}
static void native_update_found_callback(const char* version, const char* description) {
if (g_jvm == NULL || g_logCallbackObject == NULL || g_updateFoundCallbackMethod == NULL) {
return;
}

int detach_when_done;
JNIEnv* env = get_callback_env(&detach_when_done);
if (env == NULL) return;

jstring jVersion = (*env)->NewStringUTF(env, version);
jstring jDescription = (*env)->NewStringUTF(env, description);
if (jVersion != NULL && jDescription != NULL) {
(*env)->CallVoidMethod(env, g_logCallbackObject, g_updateFoundCallbackMethod, jVersion, jDescription);
}
if (jVersion != NULL) (*env)->DeleteLocalRef(env, jVersion);
if (jDescription != NULL) (*env)->DeleteLocalRef(env, jDescription);
if (detach_when_done) (*g_jvm)->DetachCurrentThread(g_jvm);
}

static void native_no_update_callback(void) {
if (g_jvm == NULL || g_logCallbackObject == NULL || g_noUpdateCallbackMethod == NULL) {
return;
}

int detach_when_done;
JNIEnv* env = get_callback_env(&detach_when_done);
if (env == NULL) return;
(*env)->CallVoidMethod(env, g_logCallbackObject, g_noUpdateCallbackMethod);
if (detach_when_done) (*g_jvm)->DetachCurrentThread(g_jvm);
}

static jint set_log_callback(JNIEnv* env, jobject callback) {
if (g_jvm == NULL && (*env)->GetJavaVM(env, &g_jvm) != JNI_OK) {
return -1;
}

// Clear existing callback
if (g_logCallbackObject != NULL) {
(*env)->DeleteGlobalRef(env, g_logCallbackObject);
g_logCallbackObject = NULL;
g_logCallbackMethod = NULL;
g_updateFoundCallbackMethod = NULL;
g_noUpdateCallbackMethod = NULL;
}

if (callback == NULL) {
// Disable callback
#ifdef __APPLE__
sparkle_set_log_callback(NULL);
sparkle_set_update_callback(NULL);
sparkle_set_no_update_callback(NULL);
#else
winsparkle_set_log_callback(NULL);
winsparkle_set_update_callback(NULL);
winsparkle_set_no_update_callback(NULL);
#endif
return 0;
}

// Create global reference to callback object
g_logCallbackObject = (*env)->NewGlobalRef(env, callback);
if (g_logCallbackObject == NULL) {
return -2;
}
if (g_logCallbackObject == NULL) return -2;

// Get the callback method
jclass callbackClass = (*env)->GetObjectClass(env, callback);
g_logCallbackMethod = (*env)->GetMethodID(env, callbackClass, "onLog", "(ILjava/lang/String;Ljava/lang/String;)V");
g_updateFoundCallbackMethod = (*env)->GetMethodID(env, callbackClass, "onUpdateFound", "(Ljava/lang/String;Ljava/lang/String;)V");
g_noUpdateCallbackMethod = (*env)->GetMethodID(env, callbackClass, "onUpdateNotFound", "()V");
(*env)->DeleteLocalRef(env, callbackClass);

if (g_logCallbackMethod == NULL) {
if (g_logCallbackMethod == NULL || g_updateFoundCallbackMethod == NULL || g_noUpdateCallbackMethod == NULL) {
(*env)->DeleteGlobalRef(env, g_logCallbackObject);
g_logCallbackObject = NULL;
g_logCallbackMethod = NULL;
g_updateFoundCallbackMethod = NULL;
g_noUpdateCallbackMethod = NULL;
return -3;
}

// Set native callback
#ifdef __APPLE__
sparkle_set_log_callback(native_log_callback);
sparkle_set_update_callback(native_update_found_callback);
sparkle_set_no_update_callback(native_no_update_callback);
#else
winsparkle_set_log_callback(native_log_callback);
winsparkle_set_update_callback(native_update_found_callback);
winsparkle_set_no_update_callback(native_no_update_callback);
#endif

return 0;
}

// JNI function to set log callback
JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_UpdateManagerBase_nativeSetLogCallback(JNIEnv* env, jobject obj, jobject callback) {
return set_log_callback(env, callback);
}

// JNI function to set shutdown callback
JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_WinSparkleUpdateManager_nativeSetShutdownCallback(JNIEnv* env, jobject obj, jobject callback) {
Expand Down Expand Up @@ -219,6 +276,11 @@ Java_org_ooni_probe_shared_SparkleUpdateManager_nativeInit(JNIEnv* env, jobject
return result;
}

JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_SparkleUpdateManager_nativeSetLogCallback(JNIEnv* env, jobject obj, jobject callback) {
return set_log_callback(env, callback);
}

JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_SparkleUpdateManager_nativeCheckForUpdates(JNIEnv* env, jobject obj, jboolean showUI) {
return sparkle_check_for_updates(showUI ? 1 : 0);
Expand Down Expand Up @@ -290,13 +352,22 @@ Java_org_ooni_probe_shared_SparkleUpdateManager_nativeSetShutdownCallback(JNIEnv
#ifdef _WIN32

JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_WinSparkleUpdateManager_nativeInit(JNIEnv* env, jobject obj, jstring appcastUrl) {
Java_org_ooni_probe_shared_WinSparkleUpdateManager_nativeInit(JNIEnv* env, jobject obj, jstring appcastUrl, jstring publicKey, jstring appVersion, jint checkIntervalHours) {
const char* url = jstring_to_cstring(env, appcastUrl);
int result = winsparkle_init(url);
const char* key = jstring_to_cstring(env, publicKey);
const char* version = jstring_to_cstring(env, appVersion);
int result = winsparkle_init(url, key, "OONI", "OONI Probe", version, checkIntervalHours);
release_cstring(env, appcastUrl, url);
release_cstring(env, publicKey, key);
release_cstring(env, appVersion, version);
return result;
}

JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_WinSparkleUpdateManager_nativeSetLogCallback(JNIEnv* env, jobject obj, jobject callback) {
return set_log_callback(env, callback);
}

JNIEXPORT jint JNICALL
Java_org_ooni_probe_shared_WinSparkleUpdateManager_nativeCheckForUpdates(JNIEnv* env, jobject obj, jboolean showUI) {
return winsparkle_check_for_updates(showUI ? 1 : 0);
Expand Down
Loading
Loading