1 /* 2 * Copyright 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "ActivityRecognitionHardware" 18 19 #include <jni.h> 20 #include <JNIHelp.h> 21 22 #include <android_runtime/AndroidRuntime.h> 23 #include <android_runtime/Log.h> 24 25 #include "activity_recognition.h" 26 27 28 // keep base connection data from the HAL 29 static activity_recognition_module_t* sModule = NULL; 30 static activity_recognition_device_t* sDevice = NULL; 31 32 static jobject sCallbacksObject = NULL; 33 static jmethodID sOnActivityChanged = NULL; 34 35 36 static void check_and_clear_exceptions(JNIEnv* env, const char* method_name) { 37 if (!env->ExceptionCheck()) { 38 return; 39 } 40 41 ALOGE("An exception was thrown by '%s'.", method_name); 42 LOGE_EX(env); 43 env->ExceptionClear(); 44 } 45 46 static jint attach_thread(JNIEnv** env) { 47 JavaVM* java_vm = android::AndroidRuntime::getJavaVM(); 48 assert(java_vm != NULL); 49 50 JavaVMAttachArgs args = { 51 JNI_VERSION_1_6, 52 "ActivityRecognition HAL callback.", 53 NULL /* group */ 54 }; 55 56 jint result = java_vm->AttachCurrentThread(env, &args); 57 if (result != JNI_OK) { 58 ALOGE("Attach to callback thread failed: %d", result); 59 } 60 61 return result; 62 } 63 64 static jint detach_thread() { 65 JavaVM* java_vm = android::AndroidRuntime::getJavaVM(); 66 assert(java_vm != NULL); 67 68 jint result = java_vm->DetachCurrentThread(); 69 if (result != JNI_OK) { 70 ALOGE("Detach of callback thread failed: %d", result); 71 } 72 73 return result; 74 } 75 76 77 /** 78 * Handle activity recognition events from HAL. 79 */ 80 static void activity_callback( 81 const activity_recognition_callback_procs_t* procs, 82 const activity_event_t* events, 83 int count) { 84 if (sOnActivityChanged == NULL) { 85 ALOGE("Dropping activity_callback because onActivityChanged handler is null."); 86 return; 87 } 88 89 if (events == NULL || count <= 0) { 90 ALOGE("Invalid activity_callback. Count: %d, Events: %p", count, events); 91 return; 92 } 93 94 JNIEnv* env = NULL; 95 int result = attach_thread(&env); 96 if (result != JNI_OK) { 97 ALOGE("Unable to attach thread with JNI."); 98 return; 99 } 100 101 jclass event_class = 102 env->FindClass("android/hardware/location/ActivityRecognitionHardware$Event"); 103 jmethodID event_ctor = env->GetMethodID(event_class, "<init>", "()V"); 104 jfieldID activity_field = env->GetFieldID(event_class, "activity", "I"); 105 jfieldID type_field = env->GetFieldID(event_class, "type", "I"); 106 jfieldID timestamp_field = env->GetFieldID(event_class, "timestamp", "J"); 107 108 jobjectArray events_array = env->NewObjectArray(count, event_class, NULL); 109 for (int i = 0; i < count; ++i) { 110 const activity_event_t* event = &events[i]; 111 jobject event_object = env->NewObject(event_class, event_ctor); 112 env->SetIntField(event_object, activity_field, event->activity); 113 env->SetIntField(event_object, type_field, event->event_type); 114 env->SetLongField(event_object, timestamp_field, event->timestamp); 115 env->SetObjectArrayElement(events_array, i, event_object); 116 env->DeleteLocalRef(event_object); 117 } 118 119 env->CallVoidMethod(sCallbacksObject, sOnActivityChanged, events_array); 120 check_and_clear_exceptions(env, __FUNCTION__); 121 122 // TODO: ideally we'd let the HAL register the callback thread only once 123 detach_thread(); 124 } 125 126 activity_recognition_callback_procs_t sCallbacks { 127 activity_callback, 128 }; 129 130 /** 131 * Initializes the ActivityRecognitionHardware class from the native side. 132 */ 133 static void class_init(JNIEnv* env, jclass clazz) { 134 // open the hardware module 135 int error = hw_get_module( 136 ACTIVITY_RECOGNITION_HARDWARE_MODULE_ID, 137 (const hw_module_t**) &sModule); 138 if (error != 0) { 139 ALOGE("Error hw_get_module: %d", error); 140 return; 141 } 142 143 error = activity_recognition_open(&sModule->common, &sDevice); 144 if (error != 0) { 145 ALOGE("Error opening device: %d", error); 146 return; 147 } 148 149 // get references to the Java provided methods 150 sOnActivityChanged = env->GetMethodID( 151 clazz, 152 "onActivityChanged", 153 "([Landroid/hardware/location/ActivityRecognitionHardware$Event;)V"); 154 if (sOnActivityChanged == NULL) { 155 ALOGE("Error obtaining ActivityChanged callback."); 156 return; 157 } 158 159 // register callbacks 160 sDevice->register_activity_callback(sDevice, &sCallbacks); 161 } 162 163 /** 164 * Initializes and connect the callbacks handlers in the HAL. 165 */ 166 static void initialize(JNIEnv* env, jobject obj) { 167 if (sCallbacksObject == NULL) { 168 sCallbacksObject = env->NewGlobalRef(obj); 169 } else { 170 ALOGD("Callbacks Object was already initialized."); 171 } 172 173 if (sDevice != NULL) { 174 sDevice->register_activity_callback(sDevice, &sCallbacks); 175 } else { 176 ALOGD("ActivityRecognition device not found during initialization."); 177 } 178 } 179 180 /** 181 * De-initializes the ActivityRecognitionHardware from the native side. 182 */ 183 static void release(JNIEnv* env, jobject obj) { 184 if (sDevice == NULL) { 185 return; 186 } 187 188 int error = activity_recognition_close(sDevice); 189 if (error != 0) { 190 ALOGE("Error closing device: %d", error); 191 return; 192 } 193 } 194 195 /** 196 * Returns true if ActivityRecognition HAL is supported, false otherwise. 197 */ 198 static jboolean is_supported(JNIEnv* env, jclass clazz) { 199 if (sModule != NULL && sDevice != NULL ) { 200 return JNI_TRUE; 201 } 202 return JNI_FALSE; 203 } 204 205 /** 206 * Gets an array representing the supported activities. 207 */ 208 static jobjectArray get_supported_activities(JNIEnv* env, jobject obj) { 209 if (sModule == NULL) { 210 return NULL; 211 } 212 213 char const* const* list = NULL; 214 int list_size = sModule->get_supported_activities_list(sModule, &list); 215 if (list_size <= 0 || list == NULL) { 216 return NULL; 217 } 218 219 jclass string_class = env->FindClass("java/lang/String"); 220 if (string_class == NULL) { 221 ALOGE("Unable to find String class for supported activities."); 222 return NULL; 223 } 224 225 jobjectArray string_array = env->NewObjectArray(list_size, string_class, NULL); 226 if (string_array == NULL) { 227 ALOGE("Unable to create string array for supported activities."); 228 return NULL; 229 } 230 231 for (int i = 0; i < list_size; ++i) { 232 const char* string_ptr = const_cast<const char*>(list[i]); 233 jstring string = env->NewStringUTF(string_ptr); 234 env->SetObjectArrayElement(string_array, i, string); 235 } 236 237 return string_array; 238 } 239 240 /** 241 * Enables a given activity event to be actively monitored. 242 */ 243 static int enable_activity_event( 244 JNIEnv* env, 245 jobject obj, 246 jint activity_handle, 247 jint event_type, 248 jlong report_latency_ns) { 249 return sDevice->enable_activity_event( 250 sDevice, 251 (uint32_t) activity_handle, 252 (uint32_t) event_type, 253 report_latency_ns); 254 } 255 256 /** 257 * Disables a given activity event from being actively monitored. 258 */ 259 static int disable_activity_event( 260 JNIEnv* env, 261 jobject obj, 262 jint activity_handle, 263 jint event_type) { 264 return sDevice->disable_activity_event( 265 sDevice, 266 (uint32_t) activity_handle, 267 (uint32_t) event_type); 268 } 269 270 /** 271 * Request flush for al batch buffers. 272 */ 273 static int flush(JNIEnv* env, jobject obj) { 274 return sDevice->flush(sDevice); 275 } 276 277 278 static JNINativeMethod sMethods[] = { 279 // {"name", "signature", (void*) functionPointer }, 280 { "nativeClassInit", "()V", (void*) class_init }, 281 { "nativeInitialize", "()V", (void*) initialize }, 282 { "nativeRelease", "()V", (void*) release }, 283 { "nativeIsSupported", "()Z", (void*) is_supported }, 284 { "nativeGetSupportedActivities", "()[Ljava/lang/String;", (void*) get_supported_activities }, 285 { "nativeEnableActivityEvent", "(IIJ)I", (void*) enable_activity_event }, 286 { "nativeDisableActivityEvent", "(II)I", (void*) disable_activity_event }, 287 { "nativeFlush", "()I", (void*) flush }, 288 }; 289 290 /** 291 * Registration method invoked in JNI load. 292 */ 293 int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env) { 294 return jniRegisterNativeMethods( 295 env, 296 "android/hardware/location/ActivityRecognitionHardware", 297 sMethods, 298 NELEM(sMethods)); 299 } 300