More HAL functionality

This change implements JNI plugs for delivering events to
WifiNative in framework. Also introduces a test utility.

Also has updated Gscan.h which is made up-to-date with the
promised functionatliy.

Change-Id: I12dd6d9f171e01e4b23ce4839b4022acd5e599cb

Conflicts:
	service/Android.mk
diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp
index 1fb2abb..638044e 100644
--- a/service/jni/com_android_server_wifi_WifiNative.cpp
+++ b/service/jni/com_android_server_wifi_WifiNative.cpp
@@ -25,6 +25,7 @@
 
 #include "wifi.h"
 #include "wifi_hal.h"
+#include "jni_helper.h"
 
 #define REPLY_BUF_SIZE 4096 // wpa_supplicant's maximum size.
 #define EVENT_BUF_SIZE 2048
@@ -141,32 +142,202 @@
     return doStringCommand(env,javaCommand);
 }
 
-static wifi_handle halHandle;
-static jboolean android_net_wifi_startHal(JNIEnv* env, jobject) {
+/* wifi_hal <==> WifiNative bridge */
+
+static jobject mObj;                            /* saved WifiNative object */
+static JavaVM *mVM;                             /* saved JVM pointer */
+
+static const char *WifiHandleVarName = "mWifiHalHandle";
+static const char *WifiIfaceHandleVarName = "mWifiIfaceHandles";
+static jmethodID OnScanResultsMethodID;
+
+static JNIEnv *getEnv() {
+    JNIEnv *env = NULL;
+    mVM->AttachCurrentThread(&env, NULL);
+    return env;
+}
+
+static wifi_handle getWifiHandle(JNIEnv *env, jobject obj) {
+    return (wifi_handle) getLongField(env, obj, WifiHandleVarName);
+}
+
+static wifi_interface_handle getIfaceHandle(JNIEnv *env, jobject obj, jint index) {
+    return (wifi_interface_handle) getLongArrayField(env, obj, WifiIfaceHandleVarName, index);
+}
+
+static void saveEventMethodIds(JNIEnv *env, jobject obj) {
+
+    jclass cls = (env)->GetObjectClass(obj);
+    if (cls == NULL) {
+        ALOGE("Error in accessing class");
+        return;
+    } else {
+        ALOGD("cls = %p", cls);
+    }
+
+    jmethodID method = env->GetMethodID(cls, "onScanResults",
+            "(I[Lcom/android/server/wifi/WifiNative$ScanResult;)V");
+
+    if (method == NULL) {
+        ALOGE("Error in getting method ID");
+        return;
+    } else {
+        ALOGD("method = %p", method);
+    }
+}
+
+static jboolean android_net_wifi_startHal(JNIEnv* env, jobject obj) {
     ALOGD("In wifi start Hal");
+    wifi_handle halHandle = getWifiHandle(env, obj);
     if (halHandle == NULL) {
         wifi_error res = wifi_initialize(&halHandle);
+        if (res == WIFI_SUCCESS) {
+            setLongField(env, obj, WifiHandleVarName, (jlong)halHandle);
+        }
+        env->GetJavaVM(&mVM);
+        mObj = env->NewGlobalRef(obj);
+        ALOGD("halHandle = %p, mVM = %p, mObj = %p", halHandle, mVM, mObj);
         return res == WIFI_SUCCESS;
+    } else {
+        return true;
     }
-    else
-        return false;
 }
 
 void android_net_wifi_hal_cleaned_up_handler(wifi_handle handle) {
     ALOGD("In wifi cleaned up handler");
-    halHandle = NULL;
+
+    JNIEnv * env = getEnv();
+    setLongField(env, mObj, WifiHandleVarName, 0);
+    env->DeleteGlobalRef(mObj);
+    mObj = NULL;
+    mVM  = NULL;
 }
 
-static void android_net_wifi_stopHal(JNIEnv* env, jobject) {
+static void android_net_wifi_stopHal(JNIEnv* env, jobject obj) {
     ALOGD("In wifi stop Hal");
+    wifi_handle halHandle = getWifiHandle(env, obj);
     wifi_cleanup(halHandle, android_net_wifi_hal_cleaned_up_handler);
 }
 
-static void android_net_wifi_waitForHalEvents(JNIEnv* env, jobject) {
-    ALOGD("In wifi waitForHalEvents");
+static void android_net_wifi_waitForHalEvents(JNIEnv* env, jobject obj) {
+
+    ALOGD("waitForHalEvents called, vm = %p, obj = %p, env = %p", mVM, mObj, env);
+
+    wifi_handle halHandle = getWifiHandle(env, obj);
+    ALOGD("halHandle = %p", halHandle);
     wifi_event_loop(halHandle);
 }
 
+static int android_net_wifi_getInterfaces(JNIEnv *env, jobject obj) {
+    int n = 0;
+    wifi_handle halHandle = getWifiHandle(env, obj);
+    wifi_interface_handle *ifaceHandles = NULL;
+    int result = wifi_get_ifaces(halHandle, &n, &ifaceHandles);
+    if (result < 0) {
+        return result;
+    }
+
+    jlongArray array = (env)->NewLongArray(n);
+    if (array == NULL) {
+        THROW(env, "Error in accessing array");
+        return 0;
+    }
+
+    jlong elems[8];
+    if (n > 8) {
+        THROW(env, "Too many interfaces");
+        return 0;
+    }
+
+    for (int i = 0; i < n; i++) {
+        elems[i] = reinterpret_cast<jlong>(ifaceHandles[i]);
+    }
+
+    env->SetLongArrayRegion(array, 0, n, elems);
+
+    setLongArrayField(env, obj, WifiIfaceHandleVarName, array);
+    return (result < 0) ? result : n;
+}
+
+static jstring android_net_wifi_getInterfaceName(JNIEnv *env, jobject obj, jint i) {
+    char buf[EVENT_BUF_SIZE];
+
+    jlong value = getLongArrayField(env, obj, WifiIfaceHandleVarName, i);
+    wifi_interface_handle handle = (wifi_interface_handle) value;
+    int result = ::wifi_get_iface_name(handle, buf, sizeof(buf));
+    if (result < 0) {
+        return NULL;
+    } else {
+        return env->NewStringUTF(buf);
+    }
+}
+
+static void onScanResults(wifi_request_id id, unsigned num_results, wifi_scan_result *results) {
+    JNIEnv *env = NULL;
+    mVM->AttachCurrentThread(&env, NULL);
+
+    ALOGD("onScanResults called, vm = %p, obj = %p, env = %p", mVM, mObj, env);
+
+    jclass clsScanResult = (env)->FindClass("com/android/server/wifi/WifiNative$ScanResult");
+    if (clsScanResult == NULL) {
+        ALOGE("Error in accessing class");
+        return;
+    }
+
+    jobjectArray scanResults = env->NewObjectArray(num_results, clsScanResult, NULL);
+    if (scanResults == NULL) {
+        ALOGE("Error in allocating array");
+        return;
+    }
+
+    for (unsigned i = 0; i < num_results; i++) {
+
+        jobject scanResult = createObject(env, "com/android/server/wifi/WifiNative$ScanResult");
+        if (scanResult == NULL) {
+            ALOGE("Error in creating scan result");
+            return;
+        }
+
+        setStringField(env, scanResult, "SSID", results[i].ssid);
+
+        char bssid[32];
+        sprintf(bssid, "%0x:%0x:%0x:%0x:%0x:%0x", results[i].bssid[0], results[i].bssid[1],
+            results[i].bssid[2], results[i].bssid[3], results[i].bssid[4], results[i].bssid[5]);
+
+        setStringField(env, scanResult, "BSSID", bssid);
+
+        setIntField(env, scanResult, "level", results[i].rssi);
+        setLongField(env, scanResult, "timestamp", results[i].ts);
+        setIntField(env, scanResult, "frequency", results[i].channel);
+
+        env->SetObjectArrayElement(scanResults, i, scanResult);
+    }
+
+    reportEvent(env, mObj, "onScanResults",
+            "(I[Lcom/android/server/wifi/WifiNative$ScanResult;)V", id, scanResults);
+}
+
+static jboolean android_net_wifi_startScan(JNIEnv *env, jobject obj, jint iface, jint id) {
+
+    wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+    ALOGD("starting scan on interface[%d] = %p", iface, handle);
+
+    wifi_scan_cmd_params params;
+    memset(&params, 0, sizeof(params));
+
+    wifi_scan_result_handler handler;
+    memset(&handler, 0, sizeof(handler));
+    handler.on_scan_results = &onScanResults;
+
+    return wifi_start_gscan(id, handle, params, handler) == WIFI_SUCCESS;
+}
+
+static jboolean android_net_wifi_stopScan(JNIEnv *env, jobject obj, jint iface, jint id) {
+    wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+    ALOGD("stopping scan on interface[%d] = %p", iface, handle);
+
+    return wifi_stop_gscan(id, handle);
+}
 
 // ----------------------------------------------------------------------------
 
@@ -192,6 +363,10 @@
     { "startHalNative", "()Z", (void*) android_net_wifi_startHal },
     { "stopHalNative", "()V", (void*) android_net_wifi_stopHal },
     { "waitForHalEventNative", "()V", (void*) android_net_wifi_waitForHalEvents },
+    { "getInterfacesNative", "()I", (void*) android_net_wifi_getInterfaces},
+    { "getInterfaceNameNative", "(I)Ljava/lang/String;", (void*) android_net_wifi_getInterfaceName},
+    { "startScanNative", "(II)Z", (void*) android_net_wifi_startScan},
+    { "stopScanNative", "(II)Z", (void*) android_net_wifi_stopScan}
 };
 
 int register_android_net_wifi_WifiNative(JNIEnv* env) {
@@ -208,4 +383,3 @@
 }
 
 }; // namespace android
-
diff --git a/service/jni/jni_helper.cpp b/service/jni/jni_helper.cpp
new file mode 100644
index 0000000..54e9023
--- /dev/null
+++ b/service/jni/jni_helper.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "wifi"
+
+#include "jni.h"
+#include <ScopedUtfChars.h>
+#include <utils/misc.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <utils/String16.h>
+
+#include "wifi.h"
+#include "wifi_hal.h"
+#include "jni_helper.h"
+
+namespace android {
+
+/* JNI Helpers for wifi_hal implementation */
+
+void throwException( JNIEnv *env, const char *message, int line )
+{
+    ALOGE("error at line %d: %s", line, message);
+
+    const char *className = "java/lang/Exception";
+
+    jclass exClass = (env)->FindClass(className );
+    if ( exClass == NULL ) {
+        ALOGE("Could not find exception class to throw error");
+        ALOGE("error at line %d: %s", line, message);
+        return;
+    }
+
+    (env)->ThrowNew(exClass, message);
+}
+
+jlong getLongField(JNIEnv *env, jobject obj, const char *name)
+{
+    jclass cls = (env)->GetObjectClass(obj);
+    jfieldID field = (env)->GetFieldID(cls, name, "J");
+    if (field == 0) {
+        THROW(env, "Error in accessing field");
+        return 0;
+    }
+
+    jlong value = (env)->GetLongField(obj, field);
+    return value;
+}
+
+jlong getLongArrayField(JNIEnv *env, jobject obj, const char *name, int index)
+{
+    jclass cls = (env)->GetObjectClass(obj);
+    jfieldID field = (env)->GetFieldID(cls, name, "[J");
+    if (field == 0) {
+        THROW(env, "Error in accessing field definition");
+        return 0;
+    }
+
+    jlongArray array = (jlongArray)(env)->GetObjectField(obj, field);
+    jlong *elem = (env)->GetLongArrayElements(array, 0);
+    if (elem == NULL) {
+        THROW(env, "Error in accessing index element");
+        return 0;
+    }
+
+    jlong value = elem[index];
+    (env)->ReleaseLongArrayElements(array, elem, 0);
+    return value;
+}
+
+void setIntField(JNIEnv *env, jobject obj, const char *name, jint value)
+{
+    jclass cls = (env)->GetObjectClass(obj);
+    if (cls == NULL) {
+        THROW(env, "Error in accessing class");
+        return;
+    }
+
+    jfieldID field = (env)->GetFieldID(cls, name, "I");
+    if (field == NULL) {
+        THROW(env, "Error in accessing field");
+        return;
+    }
+
+    (env)->SetIntField(obj, field, value);
+}
+
+void setLongField(JNIEnv *env, jobject obj, const char *name, jlong value)
+{
+    jclass cls = (env)->GetObjectClass(obj);
+    if (cls == NULL) {
+        THROW(env, "Error in accessing class");
+        return;
+    }
+
+    jfieldID field = (env)->GetFieldID(cls, name, "J");
+    if (field == NULL) {
+        THROW(env, "Error in accessing field");
+        return;
+    }
+
+    (env)->SetLongField(obj, field, value);
+}
+
+void setLongArrayField(JNIEnv *env, jobject obj, const char *name, jlongArray value)
+{
+    ALOGD("setting long array field");
+
+    jclass cls = (env)->GetObjectClass(obj);
+    if (cls == NULL) {
+        THROW(env, "Error in accessing field");
+        return;
+    } else {
+        ALOGD("cls = %p", cls);
+    }
+
+    jfieldID field = (env)->GetFieldID(cls, name, "[J");
+    if (field == NULL) {
+        THROW(env, "Error in accessing field");
+        return;
+    }
+
+    (env)->SetObjectField(obj, field, value);
+    ALOGD("array field set");
+}
+
+void setLongArrayElement(JNIEnv *env, jobject obj, const char *name, int index, jlong value)
+{
+    jclass cls = (env)->GetObjectClass(obj);
+    if (cls == NULL) {
+        THROW(env, "Error in accessing field");
+        return;
+    } else {
+        ALOGD("cls = %p", cls);
+    }
+
+    jfieldID field = (env)->GetFieldID(cls, name, "[J");
+    if (field == NULL) {
+        THROW(env, "Error in accessing field");
+        return;
+    } else {
+        ALOGD("field = %p", field);
+    }
+
+    jlongArray array = (jlongArray)(env)->GetObjectField(obj, field);
+    if (array == NULL) {
+        THROW(env, "Error in accessing array");
+        return;
+    } else {
+        ALOGD("array = %p", array);
+    }
+
+    jlong *elem = (env)->GetLongArrayElements(array, NULL);
+    if (elem == NULL) {
+        THROW(env, "Error in accessing index element");
+        return;
+    }
+
+    elem[index] = value;
+    (env)->ReleaseLongArrayElements(array, elem, 0);
+}
+
+void setObjectField(JNIEnv *env, jobject obj, const char *name, const char *type, jobject value)
+{
+    jclass cls = (env)->GetObjectClass(obj);
+    if (cls == NULL) {
+        THROW(env, "Error in accessing class");
+        return;
+    }
+
+    jfieldID field = (env)->GetFieldID(cls, name, type);
+    if (field == NULL) {
+        THROW(env, "Error in accessing field");
+        return;
+    }
+
+    (env)->SetObjectField(obj, field, value);
+}
+
+void setStringField(JNIEnv *env, jobject obj, const char *name, const char *value)
+{
+
+    jstring str = env->NewStringUTF(value);
+
+    if (str == NULL) {
+        THROW(env, "Error in accessing class");
+        return;
+    }
+
+    setObjectField(env, obj, name, "Ljava/lang/String;", str);
+}
+
+void reportEvent(JNIEnv *env, jobject obj, const char *method, const char *signature, ...)
+{
+    va_list params;
+    va_start(params, signature);
+
+    jclass cls = (env)->GetObjectClass(obj);
+    if (cls == NULL) {
+        ALOGE("Error in accessing class");
+        return;
+    }
+
+    jmethodID methodID = env->GetMethodID(cls, method, signature);
+    if (method == NULL) {
+        ALOGE("Error in getting method ID");
+        return;
+    }
+
+    env->CallVoidMethodV(obj, methodID, params);
+    va_end(params);
+}
+
+jobject createObject(JNIEnv *env, const char *className)
+{
+    jclass cls = env->FindClass(className);
+    if (cls == NULL) {
+        ALOGE("Error in finding class");
+        return NULL;
+    }
+
+    jmethodID constructor = env->GetMethodID(cls, "<init>", "()V");
+    if (constructor == NULL) {
+        ALOGE("Error in constructor ID");
+        return NULL;
+    }
+    jobject obj = env->NewObject(cls, constructor);
+    if (constructor == NULL) {
+        ALOGE("Could not create new object of %s", className);
+        return NULL;
+    }
+
+    return obj;
+}
+
+}; // namespace android
+
+
diff --git a/service/jni/jni_helper.h b/service/jni/jni_helper.h
new file mode 100644
index 0000000..e61ee0a
--- /dev/null
+++ b/service/jni/jni_helper.h
@@ -0,0 +1,20 @@
+
+namespace android {
+
+/* JNI Helpers for wifi_hal to WifiNative bridge implementation */
+
+void throwException( JNIEnv *env, const char *message, int line );
+jlong getLongField(JNIEnv *env, jobject obj, const char *name);
+jlong getLongArrayField(JNIEnv *env, jobject obj, const char *name, int index);
+void setIntField(JNIEnv *env, jobject obj, const char *name, jint value);
+void setLongField(JNIEnv *env, jobject obj, const char *name, jlong value);
+void setLongArrayField(JNIEnv *env, jobject obj, const char *name, jlongArray value);
+void setLongArrayElement(JNIEnv *env, jobject obj, const char *name, int index, jlong value);
+void setStringField(JNIEnv *env, jobject obj, const char *name, const char *value);
+void reportEvent(JNIEnv *env, jobject obj, const char *method, const char *signature, ...);
+jobject createObject(JNIEnv *env, const char *className);
+void setObjectField(JNIEnv *env, jobject obj, const char *name, const char *type, jobject value);
+
+}
+
+#define THROW(env, message)      throwException(env, message, __LINE__)