destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 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> |
Steven Moreland | 2279b25 | 2017-07-19 09:50:45 -0700 | [diff] [blame^] | 20 | #include <nativehelper/JNIHelp.h> |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 21 | |
| 22 | #include <android_runtime/AndroidRuntime.h> |
| 23 | #include <android_runtime/Log.h> |
| 24 | |
Colin Cross | 5c5b7f0 | 2017-04-20 12:20:20 -0700 | [diff] [blame] | 25 | #include <hardware/activity_recognition.h> |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 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) { |
destradaa | 3b0224d | 2014-07-16 14:28:06 -0700 | [diff] [blame] | 97 | ALOGE("Unable to attach thread with JNI."); |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 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 | |
Andreas Gampe | 0f0b491 | 2014-11-12 08:03:48 -0800 | [diff] [blame] | 126 | activity_recognition_callback_procs_t sCallbacks = { |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 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 | |
destradaa | 3b0224d | 2014-07-16 14:28:06 -0700 | [diff] [blame] | 219 | jclass string_class = env->FindClass("java/lang/String"); |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 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]); |
destradaa | 3b0224d | 2014-07-16 14:28:06 -0700 | [diff] [blame] | 233 | jstring string = env->NewStringUTF(string_ptr); |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 234 | env->SetObjectArrayElement(string_array, i, string); |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 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 | |
Daniel Micay | 76f6a86 | 2015-09-19 17:31:01 -0400 | [diff] [blame] | 278 | static const JNINativeMethod sMethods[] = { |
destradaa | a4fa3b5 | 2014-07-09 10:46:39 -0700 | [diff] [blame] | 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 | } |