android_hardware_location_ActivityRecognitionHardware.cpp revision a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26
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 29static activity_recognition_module_t* sModule = NULL; 30static activity_recognition_device_t* sDevice = NULL; 31 32static jobject sCallbacksObject = NULL; 33static jmethodID sOnActivityChanged = NULL; 34 35 36static 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 46static 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 64static 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 */ 80static 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 return; 98 } 99 100 jclass event_class = 101 env->FindClass("android/hardware/location/ActivityRecognitionHardware$Event"); 102 jmethodID event_ctor = env->GetMethodID(event_class, "<init>", "()V"); 103 jfieldID activity_field = env->GetFieldID(event_class, "activity", "I"); 104 jfieldID type_field = env->GetFieldID(event_class, "type", "I"); 105 jfieldID timestamp_field = env->GetFieldID(event_class, "timestamp", "J"); 106 107 jobjectArray events_array = env->NewObjectArray(count, event_class, NULL); 108 for (int i = 0; i < count; ++i) { 109 const activity_event_t* event = &events[i]; 110 jobject event_object = env->NewObject(event_class, event_ctor); 111 env->SetIntField(event_object, activity_field, event->activity); 112 env->SetIntField(event_object, type_field, event->event_type); 113 env->SetLongField(event_object, timestamp_field, event->timestamp); 114 env->SetObjectArrayElement(events_array, i, event_object); 115 env->DeleteLocalRef(event_object); 116 } 117 118 env->CallVoidMethod(sCallbacksObject, sOnActivityChanged, events_array); 119 check_and_clear_exceptions(env, __FUNCTION__); 120 121 // TODO: ideally we'd let the HAL register the callback thread only once 122 detach_thread(); 123} 124 125activity_recognition_callback_procs_t sCallbacks { 126 activity_callback, 127}; 128 129/** 130 * Initializes the ActivityRecognitionHardware class from the native side. 131 */ 132static void class_init(JNIEnv* env, jclass clazz) { 133 // open the hardware module 134 int error = hw_get_module( 135 ACTIVITY_RECOGNITION_HARDWARE_MODULE_ID, 136 (const hw_module_t**) &sModule); 137 if (error != 0) { 138 ALOGE("Error hw_get_module: %d", error); 139 return; 140 } 141 142 error = activity_recognition_open(&sModule->common, &sDevice); 143 if (error != 0) { 144 ALOGE("Error opening device: %d", error); 145 return; 146 } 147 148 // get references to the Java provided methods 149 sOnActivityChanged = env->GetMethodID( 150 clazz, 151 "onActivityChanged", 152 "([Landroid/hardware/location/ActivityRecognitionHardware$Event;)V"); 153 if (sOnActivityChanged == NULL) { 154 ALOGE("Error obtaining ActivityChanged callback."); 155 return; 156 } 157 158 // register callbacks 159 sDevice->register_activity_callback(sDevice, &sCallbacks); 160} 161 162/** 163 * Initializes and connect the callbacks handlers in the HAL. 164 */ 165static void initialize(JNIEnv* env, jobject obj) { 166 if (sCallbacksObject == NULL) { 167 sCallbacksObject = env->NewGlobalRef(obj); 168 } else { 169 ALOGD("Callbacks Object was already initialized."); 170 } 171 172 if (sDevice != NULL) { 173 sDevice->register_activity_callback(sDevice, &sCallbacks); 174 } else { 175 ALOGD("ActivityRecognition device not found during initialization."); 176 } 177} 178 179/** 180 * De-initializes the ActivityRecognitionHardware from the native side. 181 */ 182static void release(JNIEnv* env, jobject obj) { 183 if (sDevice == NULL) { 184 return; 185 } 186 187 int error = activity_recognition_close(sDevice); 188 if (error != 0) { 189 ALOGE("Error closing device: %d", error); 190 return; 191 } 192} 193 194/** 195 * Returns true if ActivityRecognition HAL is supported, false otherwise. 196 */ 197static jboolean is_supported(JNIEnv* env, jclass clazz) { 198 if (sModule != NULL && sDevice != NULL ) { 199 return JNI_TRUE; 200 } 201 return JNI_FALSE; 202} 203 204/** 205 * Gets an array representing the supported activities. 206 */ 207static jobjectArray get_supported_activities(JNIEnv* env, jobject obj) { 208 if (sModule == NULL) { 209 return NULL; 210 } 211 212 char const* const* list = NULL; 213 int list_size = sModule->get_supported_activities_list(sModule, &list); 214 if (list_size <= 0 || list == NULL) { 215 return NULL; 216 } 217 218 jclass string_class = env->FindClass("java/lang/String;"); 219 if (string_class == NULL) { 220 ALOGE("Unable to find String class for supported activities."); 221 return NULL; 222 } 223 224 jobjectArray string_array = env->NewObjectArray(list_size, string_class, NULL); 225 if (string_array == NULL) { 226 ALOGE("Unable to create string array for supported activities."); 227 return NULL; 228 } 229 230 for (int i = 0; i < list_size; ++i) { 231 const char* string_ptr = const_cast<const char*>(list[i]); 232 jsize string_length = strlen(string_ptr); 233 jstring string = env->NewString((const jchar*) string_ptr, string_length); 234 env->SetObjectArrayElement(string_array, i, string); 235 236 // log debugging information in case we need to try to trace issues with the strings 237 if (string_length) { 238 ALOGD("Invalid activity (index=%d) name size: %d", i, string_length); 239 } 240 } 241 242 return string_array; 243} 244 245/** 246 * Enables a given activity event to be actively monitored. 247 */ 248static int enable_activity_event( 249 JNIEnv* env, 250 jobject obj, 251 jint activity_handle, 252 jint event_type, 253 jlong report_latency_ns) { 254 return sDevice->enable_activity_event( 255 sDevice, 256 (uint32_t) activity_handle, 257 (uint32_t) event_type, 258 report_latency_ns); 259} 260 261/** 262 * Disables a given activity event from being actively monitored. 263 */ 264static int disable_activity_event( 265 JNIEnv* env, 266 jobject obj, 267 jint activity_handle, 268 jint event_type) { 269 return sDevice->disable_activity_event( 270 sDevice, 271 (uint32_t) activity_handle, 272 (uint32_t) event_type); 273} 274 275/** 276 * Request flush for al batch buffers. 277 */ 278static int flush(JNIEnv* env, jobject obj) { 279 return sDevice->flush(sDevice); 280} 281 282 283static JNINativeMethod sMethods[] = { 284 // {"name", "signature", (void*) functionPointer }, 285 { "nativeClassInit", "()V", (void*) class_init }, 286 { "nativeInitialize", "()V", (void*) initialize }, 287 { "nativeRelease", "()V", (void*) release }, 288 { "nativeIsSupported", "()Z", (void*) is_supported }, 289 { "nativeGetSupportedActivities", "()[Ljava/lang/String;", (void*) get_supported_activities }, 290 { "nativeEnableActivityEvent", "(IIJ)I", (void*) enable_activity_event }, 291 { "nativeDisableActivityEvent", "(II)I", (void*) disable_activity_event }, 292 { "nativeFlush", "()I", (void*) flush }, 293}; 294 295/** 296 * Registration method invoked in JNI load. 297 */ 298int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env) { 299 return jniRegisterNativeMethods( 300 env, 301 "android/hardware/location/ActivityRecognitionHardware", 302 sMethods, 303 NELEM(sMethods)); 304} 305