Peng Xu | 9ff7d22 | 2016-02-11 13:02:05 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016, 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 | #include "context_hub.h" |
| 18 | |
| 19 | #include <string.h> |
| 20 | #include <stdint.h> |
| 21 | #include <stdio.h> |
| 22 | |
| 23 | #include <jni.h> |
| 24 | #include "JNIHelp.h" |
| 25 | #include "core_jni_helpers.h" |
| 26 | #include "stdint.h" |
| 27 | #include "stdlib.h" |
| 28 | |
| 29 | |
| 30 | namespace android { |
| 31 | |
| 32 | namespace { |
| 33 | |
| 34 | // TODO: We should share this array_length function widely around Android |
| 35 | // code. |
| 36 | /* |
| 37 | * Finds the length of a statically-sized array using template trickery that |
| 38 | * also prevents it from being applied to the wrong type. |
| 39 | */ |
| 40 | template <typename T, size_t N> |
| 41 | constexpr size_t array_length(T (&)[N]) { return N; } |
| 42 | |
| 43 | struct jniInfo_s { |
| 44 | JavaVM *vm; |
| 45 | jclass contextHubInfoClass; |
| 46 | jclass contextHubServiceClass; |
| 47 | jclass memoryRegionsClass; |
| 48 | |
| 49 | jobject jContextHubService; |
| 50 | |
| 51 | jmethodID msgReceiptCallBack; |
| 52 | |
| 53 | jmethodID contextHubInfoCtor; |
| 54 | jmethodID contextHubInfoSetId; |
| 55 | jmethodID contextHubInfoSetName; |
| 56 | jmethodID contextHubInfoSetVendor; |
| 57 | jmethodID contextHubInfoSetToolchain; |
| 58 | jmethodID contextHubInfoSetPlatformVersion; |
| 59 | jmethodID contextHubInfoSetStaticSwVersion; |
| 60 | jmethodID contextHubInfoSetToolchainVersion; |
| 61 | jmethodID contextHubInfoSetPeakMips; |
| 62 | jmethodID contextHubInfoSetStoppedPowerDrawMw; |
| 63 | jmethodID contextHubInfoSetSleepPowerDrawMw; |
| 64 | jmethodID contextHubInfoSetPeakPowerDrawMw; |
| 65 | jmethodID contextHubInfoSetSupportedSensors; |
| 66 | jmethodID contextHubInfoSetMemoryRegions; |
| 67 | |
| 68 | jmethodID contextHubServiceMsgReceiptCallback; |
| 69 | }; |
| 70 | |
| 71 | struct context_hub_info_s { |
| 72 | int cookie; |
| 73 | int numHubs; |
| 74 | const struct context_hub_t *hubs; |
| 75 | struct context_hub_module_t *contextHubModule; |
| 76 | }; |
| 77 | |
| 78 | struct contextHubServiceDb_s { |
| 79 | int initialized; |
| 80 | context_hub_info_s hubInfo; |
| 81 | jniInfo_s jniInfo; |
| 82 | }; |
| 83 | |
| 84 | } // unnamed namespace |
| 85 | |
| 86 | static contextHubServiceDb_s db; |
| 87 | |
| 88 | int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg, |
| 89 | void *cookie); |
| 90 | |
| 91 | static void initContextHubService() { |
| 92 | int err = 0; |
| 93 | db.hubInfo.hubs = NULL; |
| 94 | db.hubInfo.numHubs = 0; |
| 95 | db.hubInfo.cookie = 0; |
| 96 | int i; |
| 97 | |
| 98 | err = hw_get_module(CONTEXT_HUB_MODULE_ID, |
| 99 | (hw_module_t const**)(&db.hubInfo.contextHubModule)); |
| 100 | |
| 101 | if (err) { |
| 102 | ALOGE("** Could not load %s module : err %s", CONTEXT_HUB_MODULE_ID, |
| 103 | strerror(-err)); |
| 104 | } |
| 105 | |
| 106 | if (db.hubInfo.contextHubModule) { |
| 107 | ALOGD("Fetching hub info"); |
| 108 | db.hubInfo.numHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule, |
| 109 | &db.hubInfo.hubs); |
| 110 | |
| 111 | if (db.hubInfo.numHubs > 0) { |
| 112 | for (i = 0; i < db.hubInfo.numHubs; i++) { |
| 113 | // TODO : Event though one cookie is OK for now, lets change |
| 114 | // this to be one per hub |
| 115 | db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id, |
| 116 | context_hub_callback, |
| 117 | &db.hubInfo.cookie); |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | static int onMessageReceipt(int *header, int headerLen, char *msg, int msgLen) { |
| 124 | JNIEnv *env; |
| 125 | if ((db.jniInfo.vm)->AttachCurrentThread(&env, NULL) != JNI_OK) { |
| 126 | return -1; |
| 127 | } |
| 128 | |
| 129 | jbyteArray jmsg = env->NewByteArray(msgLen); |
| 130 | jintArray jheader = env->NewIntArray(headerLen); |
| 131 | |
| 132 | env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg); |
| 133 | env->SetIntArrayRegion(jheader, 0, headerLen, (jint *)header); |
| 134 | |
| 135 | |
| 136 | return env->CallIntMethod(db.jniInfo.jContextHubService, |
| 137 | db.jniInfo.contextHubServiceMsgReceiptCallback, |
| 138 | jheader, jmsg); |
| 139 | } |
| 140 | |
| 141 | int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg, |
| 142 | void *cookie) { |
| 143 | int msgHeader[4]; |
| 144 | |
| 145 | msgHeader[0] = msg->message_type; |
| 146 | msgHeader[1] = 0; // TODO : HAL does not have a version field |
| 147 | msgHeader[2] = hub_id; |
| 148 | |
| 149 | onMessageReceipt(msgHeader, sizeof(msgHeader), (char *)msg->message, msg->message_len); // TODO : Populate this |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | static int init_jni(JNIEnv *env, jobject instance) { |
| 154 | |
| 155 | if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) { |
| 156 | return -1; |
| 157 | } |
| 158 | |
| 159 | db.jniInfo.jContextHubService = env->NewGlobalRef(instance); |
| 160 | |
| 161 | db.jniInfo.contextHubInfoClass = |
| 162 | env->FindClass("android/hardware/location/ContextHubInfo"); |
| 163 | |
| 164 | db.jniInfo.contextHubServiceClass = |
| 165 | env->FindClass("android/hardware/location/ContextHubService"); |
| 166 | |
| 167 | db.jniInfo.memoryRegionsClass = |
| 168 | env->FindClass("android/hardware/location/MemoryRegion"); |
| 169 | |
| 170 | //TODO :: Add error checking |
| 171 | db.jniInfo.contextHubInfoCtor = |
| 172 | env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V"); |
| 173 | db.jniInfo.contextHubInfoSetId = |
| 174 | env->GetMethodID(db.jniInfo.contextHubInfoClass, "setId", "(I)V"); |
| 175 | db.jniInfo.contextHubInfoSetName = |
| 176 | env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", |
| 177 | "(Ljava/lang/String;)V"); |
| 178 | |
| 179 | db.jniInfo.contextHubInfoSetVendor = |
| 180 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 181 | "setVendor", "(Ljava/lang/String;)V"); |
| 182 | db.jniInfo.contextHubInfoSetToolchain = |
| 183 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 184 | "setToolchain", "(Ljava/lang/String;)V"); |
| 185 | db.jniInfo.contextHubInfoSetPlatformVersion = |
| 186 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 187 | "setPlatformVersion", "(I)V"); |
| 188 | db.jniInfo.contextHubInfoSetStaticSwVersion = |
| 189 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 190 | "setStaticSwVersion", "(I)V"); |
| 191 | db.jniInfo.contextHubInfoSetToolchainVersion = |
| 192 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 193 | "setToolchainVersion", "(I)V"); |
| 194 | db.jniInfo.contextHubInfoSetPeakMips = |
| 195 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 196 | "setPeakMips", "(F)V"); |
| 197 | db.jniInfo.contextHubInfoSetStoppedPowerDrawMw = |
| 198 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 199 | "setStoppedPowerDrawMw", "(F)V"); |
| 200 | db.jniInfo.contextHubInfoSetSleepPowerDrawMw = |
| 201 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 202 | "setSleepPowerDrawMw", "(F)V"); |
| 203 | db.jniInfo.contextHubInfoSetPeakPowerDrawMw = |
| 204 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 205 | "setPeakPowerDrawMw", "(F)V"); |
| 206 | db.jniInfo.contextHubInfoSetSupportedSensors = |
| 207 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 208 | "setSupportedSensors", "([I)V"); |
| 209 | db.jniInfo.contextHubInfoSetMemoryRegions = |
| 210 | env->GetMethodID(db.jniInfo.contextHubInfoClass, |
| 211 | "setMemoryRegions", "([Landroid/hardware/location/MemoryRegion;)V"); |
| 212 | |
| 213 | |
| 214 | db.jniInfo.contextHubServiceMsgReceiptCallback = |
| 215 | env->GetMethodID(db.jniInfo.contextHubServiceClass, "onMessageReceipt", |
| 216 | "([I[B)I"); |
| 217 | db.jniInfo.contextHubInfoSetName = |
| 218 | env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", |
| 219 | "(Ljava/lang/String;)V"); |
| 220 | |
| 221 | |
| 222 | return 0; |
| 223 | } |
| 224 | |
| 225 | static jobject constructJContextHubInfo(JNIEnv *env, const struct context_hub_t *hub) { |
| 226 | jstring jstrBuf; |
| 227 | jintArray jintBuf; |
| 228 | jobjectArray jmemBuf; |
| 229 | |
| 230 | int dummyConnectedSensors[] = {1, 2, 3, 4, 5}; |
| 231 | |
| 232 | jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass, |
| 233 | db.jniInfo.contextHubInfoCtor); |
| 234 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub->hub_id); |
| 235 | |
| 236 | jstrBuf = env->NewStringUTF(hub->name); |
| 237 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf); |
| 238 | |
| 239 | jstrBuf = env->NewStringUTF(hub->vendor); |
| 240 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf); |
| 241 | |
| 242 | jstrBuf = env->NewStringUTF(hub->toolchain); |
| 243 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf); |
| 244 | |
| 245 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub->platform_version); |
| 246 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub->toolchain_version); |
| 247 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub->peak_mips); |
| 248 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw, hub->stopped_power_draw_mw); |
| 249 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw, hub->sleep_power_draw_mw); |
| 250 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw, hub->peak_power_draw_mw); |
| 251 | |
| 252 | // TODO : jintBuf = env->NewIntArray(hub->num_connected_sensors); |
| 253 | // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, hub->connected_sensors); |
| 254 | jintBuf = env->NewIntArray(array_length(dummyConnectedSensors)); |
| 255 | env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, dummyConnectedSensors); |
| 256 | |
| 257 | // We are not getting the memory regions from the CH Hal - change this when it is available |
| 258 | jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, NULL); |
| 259 | // Note the zero size above. We do not need to set any elements |
| 260 | env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf); |
| 261 | |
| 262 | return jHub; |
| 263 | } |
| 264 | |
| 265 | static jobjectArray nativeInitialize(JNIEnv *env, jobject instance) |
| 266 | { |
| 267 | jobject hub; |
| 268 | jobjectArray retArray; |
| 269 | |
| 270 | initContextHubService(); |
| 271 | |
| 272 | if (init_jni(env, instance) < 0) { |
| 273 | return NULL; |
| 274 | } |
| 275 | |
| 276 | // Note : The service is clamping the number of hubs to 1 |
| 277 | db.hubInfo.numHubs = 1; |
| 278 | |
| 279 | initContextHubService(); |
| 280 | |
| 281 | retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, NULL); |
| 282 | |
| 283 | for(int i = 0; i < db.hubInfo.numHubs; i++) { |
| 284 | hub = constructJContextHubInfo(env, &db.hubInfo.hubs[i]); |
| 285 | env->SetObjectArrayElement(retArray, i, hub); |
| 286 | } |
| 287 | |
| 288 | return retArray; |
| 289 | } |
| 290 | |
| 291 | static jint nativeSendMessage(JNIEnv *env, jobject instance, jintArray header_, |
| 292 | jbyteArray data_) { |
| 293 | hub_message_t msg; |
| 294 | hub_app_name_t dest; |
| 295 | uint8_t os_name[8]; |
| 296 | |
| 297 | memset(os_name, 0, sizeof(os_name)); |
| 298 | |
| 299 | jint *header = env->GetIntArrayElements(header_, 0); |
| 300 | //int numHeaderElements = env->GetArrayLength(header_); |
| 301 | jbyte *data = env->GetByteArrayElements(data_, 0); |
| 302 | int dataBufferLength = env->GetArrayLength(data_); |
| 303 | |
| 304 | /* Assume an int - thats all we understand */ |
| 305 | dest.app_name_len = array_length(os_name); // TODO : Check this |
| 306 | //dest.app_name = &header[1]; |
| 307 | dest.app_name = os_name; |
| 308 | |
| 309 | msg.app = &dest; |
| 310 | |
| 311 | msg.message_type = header[3]; |
| 312 | msg.message_len = dataBufferLength; |
| 313 | msg.message = data; |
| 314 | |
| 315 | jint retVal = db.hubInfo.contextHubModule->send_message(header[0], &msg); |
| 316 | |
| 317 | env->ReleaseIntArrayElements(header_, header, 0); |
| 318 | env->ReleaseByteArrayElements(data_, data, 0); |
| 319 | |
| 320 | return retVal; |
| 321 | } |
| 322 | |
| 323 | //-------------------------------------------------------------------------------------------------- |
| 324 | // |
| 325 | static const JNINativeMethod gContextHubServiceMethods[] = { |
| 326 | {"nativeInitialize", |
| 327 | "()[Landroid/hardware/location/ContextHubInfo;", |
| 328 | (void*)nativeInitialize }, |
| 329 | {"nativeSendMessage", |
| 330 | "([I[B)I", |
| 331 | (void*)nativeSendMessage } |
| 332 | }; |
| 333 | |
| 334 | }//namespace android |
| 335 | |
| 336 | using namespace android; |
| 337 | |
| 338 | int register_android_hardware_location_ContextHubService(JNIEnv *env) |
| 339 | { |
| 340 | RegisterMethodsOrDie(env, "android/hardware/location/ContextHubService", |
| 341 | gContextHubServiceMethods, NELEM(gContextHubServiceMethods)); |
| 342 | |
| 343 | return 0; |
| 344 | } |