1 #include "wakelock_internal.h"
2 #include "belle-sip/utils.h"
3 #include <pthread.h>
4 
5 struct _WakeLock {
6 	JavaVM *jvm;
7 	jobject powerManager;
8 	pthread_key_t jniEnvKey;
9 	jint PARTIAL_WAKE_LOCK;
10 	jmethodID newWakeLockID;
11 	jmethodID acquireID;
12 	jmethodID releaseID;
13 };
14 
15 typedef struct _WakeLock WakeLock;
16 
17 static WakeLock ctx = {
18 	.jvm = NULL,
19 	.powerManager = NULL
20 };
21 
22 static JNIEnv *get_jni_env(void);
23 static void jni_key_cleanup(void *data);
24 
belle_sip_wake_lock_init(JNIEnv * env,jobject pm)25 void belle_sip_wake_lock_init(JNIEnv *env, jobject pm) {
26 	if (ctx.jvm == NULL) {
27 		jclass powerManagerClass;
28 		jclass wakeLockClass;
29 		jfieldID fieldID;
30 
31 		(*env)->GetJavaVM(env, &ctx.jvm);
32 		pthread_key_create(&ctx.jniEnvKey, jni_key_cleanup);
33 
34 		powerManagerClass = (*env)->FindClass(env, "android/os/PowerManager");
35 		wakeLockClass = (*env)->FindClass(env, "android/os/PowerManager$WakeLock");
36 		fieldID = (*env)->GetStaticFieldID(env, powerManagerClass, "PARTIAL_WAKE_LOCK", "I");
37 
38 		ctx.PARTIAL_WAKE_LOCK = (*env)->GetStaticIntField(env, powerManagerClass, fieldID);
39 		ctx.newWakeLockID = (*env)->GetMethodID(env, powerManagerClass, "newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;");
40 		ctx.acquireID = (*env)->GetMethodID(env, wakeLockClass, "acquire", "()V");
41 		ctx.releaseID = (*env)->GetMethodID(env, wakeLockClass, "release", "()V");
42 
43 		belle_sip_message("bellesip_wake_lock_init(): initialization succeed");
44 	} else {
45 		belle_sip_warning("bellesip_wake_lock_init(): the wakelock system has already been initialized");
46 	}
47 	if (ctx.powerManager == NULL) {
48 		ctx.powerManager = (*env)->NewGlobalRef(env, pm);
49 	}
50 }
51 
belle_sip_wake_lock_uninit(JNIEnv * env)52 void belle_sip_wake_lock_uninit(JNIEnv *env) {
53 	ctx.jvm = NULL;
54 	if(ctx.powerManager != NULL) {
55 		(*env)->DeleteGlobalRef(env, ctx.powerManager);
56 		ctx.powerManager = NULL;
57 	}
58 }
59 
60 /**
61  * @brief Callback called when a thread terminates it-self.
62  * It detaches the thread from the JVM.
63  * @param data Unused.
64  */
jni_key_cleanup(void * data)65 static void jni_key_cleanup(void *data) {
66 	JNIEnv *env = (JNIEnv*) data;
67 	belle_sip_message("Thread end. Cleanup wake lock jni environment");
68 	if (env != NULL) {
69 		if (ctx.jvm != NULL) {
70 			(*ctx.jvm)->DetachCurrentThread(ctx.jvm);
71 			pthread_setspecific(ctx.jniEnvKey, NULL);
72 		} else {
73 			belle_sip_error("Wake lock cleanup. No JVM found");
74 		}
75 	}
76 }
77 
78 /**
79  * @brief Get a JNI environment.
80  * Get a JNI by attaching the current thread
81  * to the internaly stored JVM. That function can be safely
82  * called several times.
83  * @return JNI environement pointer. NULL if the function fails.
84  */
get_jni_env(void)85 static JNIEnv *get_jni_env(void) {
86 	JNIEnv *jenv = NULL;
87 	if(ctx.jvm != NULL) {
88 		jenv = pthread_getspecific(ctx.jniEnvKey);
89 		if(jenv == NULL) {
90 			if((*ctx.jvm)->AttachCurrentThread(ctx.jvm, &jenv, NULL) == JNI_OK) {
91 				pthread_setspecific(ctx.jniEnvKey, jenv);
92 				belle_sip_message("get_jni_env(): thread successfuly attached");
93 			} else {
94 				belle_sip_fatal("get_jni_env(): thread could not be attached to the JVM");
95 				jenv = NULL;
96 			}
97 		}
98 	} else {
99 		belle_sip_error("get_jni_env(): no JVM found");
100 	}
101 	return jenv;
102 }
103 
wake_lock_acquire(const char * tag)104 unsigned long wake_lock_acquire(const char *tag) {
105 	if(ctx.jvm != NULL && ctx.powerManager != NULL) {
106 		JNIEnv *env;
107 		if((env = get_jni_env())) {
108 			jstring tagString = (*env)->NewStringUTF(env, tag);
109 			jobject lock = (*env)->CallObjectMethod(env, ctx.powerManager, ctx.newWakeLockID, ctx.PARTIAL_WAKE_LOCK, tagString);
110 			(*env)->DeleteLocalRef(env, tagString);
111 			if(lock != NULL) {
112 				(*env)->CallVoidMethod(env, lock, ctx.acquireID);
113 				lock = (*env)->NewGlobalRef(env, lock);
114 				belle_sip_message("bellesip_wake_lock_acquire(): Android wake lock acquired [ref=%p]", (void *)lock);
115 				return (unsigned long)lock;
116 			} else {
117 				belle_sip_message("bellesip_wake_lock_acquire(): wake lock creation failed");
118 			}
119 		} else {
120 			belle_sip_error("bellesip_wake_lock_acquire(): cannot attach current thread to the JVM");
121 		}
122 	} else {
123 		if (ctx.jvm == NULL)
124 			belle_sip_error("bellesip_wake_lock_acquire(): cannot acquire wake lock. No JVM found");
125 		else
126 			belle_sip_error("bellesip_wake_lock_acquire(): cannot acquire wake lock. No PowerManager found");
127 	}
128 	return 0;
129 }
130 
131 
wake_lock_release(unsigned long id)132 void wake_lock_release(unsigned long id) {
133 	if(ctx.jvm != NULL && ctx.powerManager != NULL) {
134 		if(id != 0) {
135 			jobject lock = (jobject)id;
136 			JNIEnv *env;
137 			if((env = get_jni_env())) {
138 				(*env)->CallVoidMethod(env, lock, ctx.releaseID);
139 				belle_sip_message("bellesip_wake_lock_release(): Android wake lock released [ref=%p]", (void *)lock);
140 				(*env)->DeleteGlobalRef(env, lock);
141 			} else {
142 				belle_sip_error("bellesip_wake_lock_release(): cannot attach current thread to the JVM");
143 			}
144 		}
145 	} else {
146 		if(ctx.jvm == NULL)
147 			belle_sip_error("bellesip_wake_lock_release(): cannot release wake lock. No JVM found");
148 		else
149 			belle_sip_error("bellesip_wake_lock_release(): cannot release wake lock. No PowerManager found");
150 	}
151 }
152