wakelock.c 5.76 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * Copyright (c) 2012-2019 Belledonne Communications SARL.
 *
 * This file is part of belle-sip.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
19

20 21
#include "wakelock_internal.h"
#include "belle-sip/utils.h"
22
#include <pthread.h>
23

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
struct _WakeLock {
	JavaVM *jvm;
	jobject powerManager;
	pthread_key_t jniEnvKey;
	jint PARTIAL_WAKE_LOCK;
	jmethodID newWakeLockID;
	jmethodID acquireID;
	jmethodID releaseID;
};

typedef struct _WakeLock WakeLock;

static WakeLock ctx = {
	.jvm = NULL,
	.powerManager = NULL
};
40

41 42 43
static JNIEnv *get_jni_env(void);
static void jni_key_cleanup(void *data);

44 45 46 47 48 49 50 51 52 53
static void belle_sip_set_jvm(JNIEnv *env) {
	if (ctx.jvm == NULL) {
		(*env)->GetJavaVM(env, &ctx.jvm);
		pthread_key_create(&ctx.jniEnvKey, jni_key_cleanup);
		belle_sip_message("belle_sip_set_jvm(): initialization succeed");
	} else {
		belle_sip_warning("belle_sip_set_jvm(): the JNIEnv has already been initialized");
	}
}

54
void belle_sip_wake_lock_init(JNIEnv *env, jobject pm) {
55
	if (ctx.jvm == NULL) {
56 57 58 59
		belle_sip_set_jvm(env);
	}

	if (ctx.powerManager == NULL) {
60 61 62 63 64 65 66
		jclass powerManagerClass;
		jclass wakeLockClass;
		jfieldID fieldID;

		powerManagerClass = (*env)->FindClass(env, "android/os/PowerManager");
		wakeLockClass = (*env)->FindClass(env, "android/os/PowerManager$WakeLock");
		fieldID = (*env)->GetStaticFieldID(env, powerManagerClass, "PARTIAL_WAKE_LOCK", "I");
67 68 69 70 71 72

		ctx.PARTIAL_WAKE_LOCK = (*env)->GetStaticIntField(env, powerManagerClass, fieldID);
		ctx.newWakeLockID = (*env)->GetMethodID(env, powerManagerClass, "newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;");
		ctx.acquireID = (*env)->GetMethodID(env, wakeLockClass, "acquire", "()V");
		ctx.releaseID = (*env)->GetMethodID(env, wakeLockClass, "release", "()V");

73
		ctx.powerManager = (*env)->NewGlobalRef(env, pm);
74
		belle_sip_message("bellesip_wake_lock_init(): initialization succeed");
75
	} else {
François Grisez's avatar
François Grisez committed
76
		belle_sip_warning("bellesip_wake_lock_init(): the wakelock system has already been initialized");
77 78 79
	}
}

80
void belle_sip_wake_lock_uninit(JNIEnv *env) {
81
	if (ctx.powerManager != NULL) {
82 83
		(*env)->DeleteGlobalRef(env, ctx.powerManager);
		ctx.powerManager = NULL;
84 85 86
	}
}

François Grisez's avatar
François Grisez committed
87 88 89 90 91
/**
 * @brief Callback called when a thread terminates it-self.
 * It detaches the thread from the JVM.
 * @param data Unused.
 */
92
static void jni_key_cleanup(void *data) {
93
	JNIEnv *env = (JNIEnv*) data;
94
	belle_sip_message("Thread end. Cleanup wake lock jni environment");
95 96
	if (env != NULL) {
		if (ctx.jvm != NULL) {
97 98
			(*ctx.jvm)->DetachCurrentThread(ctx.jvm);
			pthread_setspecific(ctx.jniEnvKey, NULL);
99
		} else {
100
			belle_sip_error("Wake lock cleanup. No JVM found");
101 102 103 104
		}
	}
}

François Grisez's avatar
François Grisez committed
105 106 107 108 109 110 111
/**
 * @brief Get a JNI environment.
 * Get a JNI by attaching the current thread
 * to the internaly stored JVM. That function can be safely
 * called several times.
 * @return JNI environement pointer. NULL if the function fails.
 */
112 113
static JNIEnv *get_jni_env(void) {
	JNIEnv *jenv = NULL;
114 115
	if(ctx.jvm != NULL) {
		jenv = pthread_getspecific(ctx.jniEnvKey);
116
		if(jenv == NULL) {
117 118
			if((*ctx.jvm)->AttachCurrentThread(ctx.jvm, &jenv, NULL) == JNI_OK) {
				pthread_setspecific(ctx.jniEnvKey, jenv);
119 120 121 122 123 124 125 126 127 128 129 130
				belle_sip_message("get_jni_env(): thread successfuly attached");
			} else {
				belle_sip_fatal("get_jni_env(): thread could not be attached to the JVM");
				jenv = NULL;
			}
		}
	} else {
		belle_sip_error("get_jni_env(): no JVM found");
	}
	return jenv;
}

131
unsigned long wake_lock_acquire(const char *tag) {
132
	if(ctx.jvm != NULL && ctx.powerManager != NULL) {
133
		JNIEnv *env;
134
		if((env = get_jni_env())) {
135
			jstring tagString = (*env)->NewStringUTF(env, tag);
136
			jobject lock = (*env)->CallObjectMethod(env, ctx.powerManager, ctx.newWakeLockID, ctx.PARTIAL_WAKE_LOCK, tagString);
137
			(*env)->DeleteLocalRef(env, tagString);
138 139
			if(lock != NULL) {
				(*env)->CallVoidMethod(env, lock, ctx.acquireID);
jehan's avatar
jehan committed
140 141 142 143
				jobject lock2 = (*env)->NewGlobalRef(env, lock);
				(*env)->DeleteLocalRef(env, lock);
				belle_sip_message("bellesip_wake_lock_acquire(): Android wake lock [%s] acquired [ref=%p]", tag, (void *)lock2);
				return (unsigned long)lock2;
144
			} else {
Erwan Croze's avatar
Erwan Croze committed
145
				belle_sip_message("bellesip_wake_lock_acquire(): wake lock creation failed");
146
			}
147
		} else {
François Grisez's avatar
François Grisez committed
148
			belle_sip_error("bellesip_wake_lock_acquire(): cannot attach current thread to the JVM");
149 150
		}
	} else {
Erwan Croze's avatar
Erwan Croze committed
151 152 153 154
		if (ctx.jvm == NULL)
			belle_sip_error("bellesip_wake_lock_acquire(): cannot acquire wake lock. No JVM found");
		else
			belle_sip_error("bellesip_wake_lock_acquire(): cannot acquire wake lock. No PowerManager found");
155 156 157 158
	}
	return 0;
}

159

160
void wake_lock_release(unsigned long id) {
161
	if(ctx.jvm != NULL && ctx.powerManager != NULL) {
162 163 164
		if(id != 0) {
			jobject lock = (jobject)id;
			JNIEnv *env;
165
			if((env = get_jni_env())) {
166
				(*env)->CallVoidMethod(env, lock, ctx.releaseID);
Erwan Croze's avatar
Erwan Croze committed
167
				belle_sip_message("bellesip_wake_lock_release(): Android wake lock released [ref=%p]", (void *)lock);
François Grisez's avatar
François Grisez committed
168
				(*env)->DeleteGlobalRef(env, lock);
169
			} else {
François Grisez's avatar
François Grisez committed
170
				belle_sip_error("bellesip_wake_lock_release(): cannot attach current thread to the JVM");
171 172 173
			}
		}
	} else {
Erwan Croze's avatar
Erwan Croze committed
174 175 176 177
		if(ctx.jvm == NULL)
			belle_sip_error("bellesip_wake_lock_release(): cannot release wake lock. No JVM found");
		else
			belle_sip_error("bellesip_wake_lock_release(): cannot release wake lock. No PowerManager found");
178 179
	}
}