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/Android.mk b/service/Android.mk
index 25adc36..2659732 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -82,9 +82,47 @@
 
 LOCAL_STATIC_LIBRARIES += libwifi-hal
 
-LOCAL_SRC_FILES := jni/com_android_server_wifi_WifiNative.cpp
+LOCAL_SRC_FILES := \
+	jni/com_android_server_wifi_WifiNative.cpp \
+	jni/jni_helper.cpp
+
 LOCAL_MODULE := libwifi-service
 
 include $(BUILD_SHARED_LIBRARY)
 
-endif
+# Build the halutil
+# ============================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_REQUIRED_MODULES := libandroid_runtime libhardware_legacy
+
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
+LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
+LOCAL_CPPFLAGS += -Wno-conversion-null
+
+LOCAL_C_INCLUDES += \
+	libcore/include \
+	$(LOCAL_PATH)/lib
+
+LOCAL_SHARES_LIBRARIES += \
+	libcutils \
+	libutils \
+	libandroid_runtime
+
+LOCAL_STATIC_LIBRARIES := libwifi-hal libnl_2
+
+LOCAL_SHARED_LIBRARIES += \
+	libnativehelper \
+	libcutils \
+	libutils \
+	libhardware \
+	libhardware_legacy
+
+LOCAL_SRC_FILES := \
+	tools/halutil/halutil.cpp
+
+LOCAL_MODULE := halutil
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 196d74e..161f0f8 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -87,12 +87,6 @@
 
     private native void closeSupplicantConnectionNative();
 
-    public native static boolean startHalNative();
-
-    public native static void stopHalNative();
-
-    public native static void waitForHalEventNative();
-
     /**
      * Wait for the supplicant to send an event, returning the event string.
      * @return the event string sent by the supplicant.
@@ -1024,6 +1018,17 @@
     }
 
 
+    /* WIFI HAL support */
+
+    private long mWifiHalHandle;                        /* used by JNI to save wifi_handle */
+    private long[] mWifiIfaceHandles;                   /* used by JNI to save interface handles */
+
+    public native boolean startHalNative();
+
+    public native void stopHalNative();
+
+    public native void waitForHalEventNative();
+
     private class MonitorThread extends Thread {
         public void run() {
             waitForHalEventNative();
@@ -1041,4 +1046,60 @@
     public void stopHal() {
         stopHalNative();
     }
+
+    private native int getInterfacesNative();
+
+    public int getInterfaces() {
+        return getInterfacesNative();
+    }
+
+    private native String getInterfaceNameNative(int index);
+
+    public void printInterfaceNames() {
+        for (int i = 0; i < mWifiIfaceHandles.length; i++) {
+            String name = getInterfaceNameNative(i);
+            Log.i(mTAG, "interface[" + i + "] = " + name);
+        }
+    }
+
+    private native boolean startScanNative(int iface, int id);
+    private native boolean stopScanNative(int iface, int id);
+
+    public static class ScanResult {
+        public String SSID;
+        public String BSSID;
+        public String capabilities;
+        public int level;
+        public int frequency;
+        public long timestamp;
+    }
+
+    void onScanResults(int id, ScanResult[] results) {
+
+        /* !! This gets called on a different thread !! */
+
+        for (int i = 0; i < results.length; i++) {
+            Log.i(mTAG, "results[" + i + "].ssid = " + results[i].SSID);
+        }
+    }
+
+    private int mScanCmdId = 0;
+
+    public boolean startScan() {
+        synchronized (mLock) {
+            if (mScanCmdId != 0) {
+                return false;
+            } else {
+                mScanCmdId = getNewCmdIdLocked();
+            }
+        }
+
+        return startScanNative(0, mScanCmdId);          // results are reported by onScanResults
+    }
+
+    public void stopScan() {
+        synchronized (mLock) {
+            stopScanNative(0, mScanCmdId);
+        }
+    }
 }
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__)
diff --git a/service/lib/common.cpp b/service/lib/common.cpp
index 9a7f447..ab99356 100644
--- a/service/lib/common.cpp
+++ b/service/lib/common.cpp
@@ -89,6 +89,8 @@
 {
     hal_info *info = (hal_info *)handle;
 
+    ALOGD("registering command %d", id);
+
     if (info->num_cmd < info->alloc_cmd) {
         info->cmd[info->num_cmd].id   = id;
         info->cmd[info->num_cmd].cmd  = cmd;
@@ -104,6 +106,8 @@
 {
     hal_info *info = (hal_info *)handle;
 
+    ALOGD("un-registering command %d", id);
+
     for (int i = 0; i < info->num_cmd; i++) {
         if (info->cmd[i].id == id) {
             WifiCommand *cmd = info->cmd[i].cmd;
diff --git a/service/lib/cpp_bindings.cpp b/service/lib/cpp_bindings.cpp
index 28d2552..08dd754 100644
--- a/service/lib/cpp_bindings.cpp
+++ b/service/lib/cpp_bindings.cpp
@@ -17,15 +17,191 @@
 #include <netlink-types.h>
 
 #include <linux/nl80211.h>
+#include <ctype.h>
 
 #include "wifi_hal.h"
 #include "common.h"
 #include "cpp_bindings.h"
 
+#define min(x,y)        (((x) < (y)) ? (x) : (y))
+#define max(x,y)        (((x) < (y)) ? (y) : (x))
+
+void appendFmt(char *buf, int &offset, const char *fmt, ...)
+{
+    va_list params;
+    va_start(params, fmt);
+    offset += vsprintf(buf + offset, fmt, params);
+    va_end(params);
+}
+
+#define C2S(x)  case x: return #x;
+
+static const char *cmdToString(int cmd)
+{
+	switch (cmd) {
+	C2S(NL80211_CMD_UNSPEC)
+	C2S(NL80211_CMD_GET_WIPHY)
+	C2S(NL80211_CMD_SET_WIPHY)
+	C2S(NL80211_CMD_NEW_WIPHY)
+	C2S(NL80211_CMD_DEL_WIPHY)
+	C2S(NL80211_CMD_GET_INTERFACE)
+	C2S(NL80211_CMD_SET_INTERFACE)
+	C2S(NL80211_CMD_NEW_INTERFACE)
+	C2S(NL80211_CMD_DEL_INTERFACE)
+	C2S(NL80211_CMD_GET_KEY)
+	C2S(NL80211_CMD_SET_KEY)
+	C2S(NL80211_CMD_NEW_KEY)
+	C2S(NL80211_CMD_DEL_KEY)
+	C2S(NL80211_CMD_GET_BEACON)
+	C2S(NL80211_CMD_SET_BEACON)
+	C2S(NL80211_CMD_START_AP)
+	C2S(NL80211_CMD_STOP_AP)
+	C2S(NL80211_CMD_GET_STATION)
+	C2S(NL80211_CMD_SET_STATION)
+	C2S(NL80211_CMD_NEW_STATION)
+	C2S(NL80211_CMD_DEL_STATION)
+	C2S(NL80211_CMD_GET_MPATH)
+	C2S(NL80211_CMD_SET_MPATH)
+	C2S(NL80211_CMD_NEW_MPATH)
+	C2S(NL80211_CMD_DEL_MPATH)
+	C2S(NL80211_CMD_SET_BSS)
+	C2S(NL80211_CMD_SET_REG)
+	C2S(NL80211_CMD_REQ_SET_REG)
+	C2S(NL80211_CMD_GET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
+	C2S(NL80211_CMD_GET_REG)
+	C2S(NL80211_CMD_GET_SCAN)
+	C2S(NL80211_CMD_TRIGGER_SCAN)
+	C2S(NL80211_CMD_NEW_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCAN_ABORTED)
+	C2S(NL80211_CMD_REG_CHANGE)
+	C2S(NL80211_CMD_AUTHENTICATE)
+	C2S(NL80211_CMD_ASSOCIATE)
+	C2S(NL80211_CMD_DEAUTHENTICATE)
+	C2S(NL80211_CMD_DISASSOCIATE)
+	C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
+	C2S(NL80211_CMD_REG_BEACON_HINT)
+	C2S(NL80211_CMD_JOIN_IBSS)
+	C2S(NL80211_CMD_LEAVE_IBSS)
+	C2S(NL80211_CMD_TESTMODE)
+	C2S(NL80211_CMD_CONNECT)
+	C2S(NL80211_CMD_ROAM)
+	C2S(NL80211_CMD_DISCONNECT)
+	C2S(NL80211_CMD_SET_WIPHY_NETNS)
+	C2S(NL80211_CMD_GET_SURVEY)
+	C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
+	C2S(NL80211_CMD_SET_PMKSA)
+	C2S(NL80211_CMD_DEL_PMKSA)
+	C2S(NL80211_CMD_FLUSH_PMKSA)
+	C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
+	C2S(NL80211_CMD_REGISTER_FRAME)
+	C2S(NL80211_CMD_FRAME)
+	C2S(NL80211_CMD_FRAME_TX_STATUS)
+	C2S(NL80211_CMD_SET_POWER_SAVE)
+	C2S(NL80211_CMD_GET_POWER_SAVE)
+	C2S(NL80211_CMD_SET_CQM)
+	C2S(NL80211_CMD_NOTIFY_CQM)
+	C2S(NL80211_CMD_SET_CHANNEL)
+	C2S(NL80211_CMD_SET_WDS_PEER)
+	C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
+	C2S(NL80211_CMD_JOIN_MESH)
+	C2S(NL80211_CMD_LEAVE_MESH)
+	C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
+	C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
+	C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
+	C2S(NL80211_CMD_GET_WOWLAN)
+	C2S(NL80211_CMD_SET_WOWLAN)
+	C2S(NL80211_CMD_START_SCHED_SCAN)
+	C2S(NL80211_CMD_STOP_SCHED_SCAN)
+	C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
+	C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
+	C2S(NL80211_CMD_PMKSA_CANDIDATE)
+	C2S(NL80211_CMD_TDLS_OPER)
+	C2S(NL80211_CMD_TDLS_MGMT)
+	C2S(NL80211_CMD_UNEXPECTED_FRAME)
+	C2S(NL80211_CMD_PROBE_CLIENT)
+	C2S(NL80211_CMD_REGISTER_BEACONS)
+	C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
+	C2S(NL80211_CMD_SET_NOACK_MAP)
+	C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
+	C2S(NL80211_CMD_START_P2P_DEVICE)
+	C2S(NL80211_CMD_STOP_P2P_DEVICE)
+	C2S(NL80211_CMD_CONN_FAILED)
+	C2S(NL80211_CMD_SET_MCAST_RATE)
+	C2S(NL80211_CMD_SET_MAC_ACL)
+	C2S(NL80211_CMD_RADAR_DETECT)
+	C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
+	C2S(NL80211_CMD_UPDATE_FT_IES)
+	C2S(NL80211_CMD_FT_EVENT)
+	C2S(NL80211_CMD_CRIT_PROTOCOL_START)
+	C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
+	//C2S(NL80211_CMD_GET_COALESCE)
+	//C2S(NL80211_CMD_SET_COALESCE)
+	//C2S(NL80211_CMD_CHANNEL_SWITCH)
+	//C2S(NL80211_CMD_VENDOR)
+	//C2S(NL80211_CMD_SET_QOS_MAP)
+	default:
+		return "NL80211_CMD_UNKNOWN";
+	}
+}
+
+void WifiEvent::log() {
+    parse();
+
+    byte *data = (byte *)genlmsg_attrdata(mHeader, 0);
+    int len = genlmsg_attrlen(mHeader, 0);
+    ALOGD("cmd = %s, len = %d", get_cmdString(), len);
+
+    for (int i = 0; i < len; i += 16) {
+        char line[81];
+        int linelen = min(16, len - i);
+        int offset = 0;
+        appendFmt(line, offset, "%02x", data[i]);
+        for (int j = 1; j < linelen; j++) {
+            appendFmt(line, offset, " %02x", data[i+j]);
+        }
+
+        for (int j = linelen; j < 16; j++) {
+            appendFmt(line, offset, "   ");
+        }
+
+        line[23] = '-';
+
+        appendFmt(line, offset, "  ");
+
+        for (int j = 0; j < linelen; j++) {
+            if (isprint(data[i+j])) {
+                appendFmt(line, offset, "%c", data[i+j]);
+            } else {
+                appendFmt(line, offset, "-");
+            }
+        }
+
+        ALOGD(line);
+    }
+
+    ALOGD("-- End of message --");
+}
+
+const char *WifiEvent::get_cmdString() {
+    return cmdToString(get_cmd());
+}
+
+
 int WifiEvent::parse() {
+    if (mHeader != NULL) {
+        return WIFI_SUCCESS;
+    }
     mHeader = (genlmsghdr *)nlmsg_data(nlmsg_hdr(mMsg));
-    return nla_parse(mAttributes, NL80211_ATTR_MAX, genlmsg_attrdata(mHeader, 0),
+    int result = nla_parse(mAttributes, NL80211_ATTR_MAX_INTERNAL, genlmsg_attrdata(mHeader, 0),
           genlmsg_attrlen(mHeader, 0), NULL);
+
+    ALOGD("event len = %d", nlmsg_hdr(mMsg)->nlmsg_len);
+    return result;
 }
 
 int WifiRequest::create(int family, uint8_t cmd, int flags, int hdrlen) {
@@ -90,6 +266,9 @@
 }
 
 int WifiCommand::requestEvent(int cmd) {
+
+    ALOGD("requesting event %d", cmd);
+
     int res = wifi_register_handler(mInfo, cmd, event_handler, this);
     if (res < 0) {
         return res;
@@ -99,10 +278,13 @@
     if (res < 0)
         goto out;
 
+    ALOGD("waiting for response %d", cmd);
+
     res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage());    /* send message */
     if (res < 0)
         goto out;
 
+    ALOGD("waiting for event %d", cmd);
     res = mCondition.wait();
     if (res < 0)
         goto out;
@@ -154,7 +336,7 @@
     WifiEvent event(msg);
     int res = event.parse();
     if (res < 0) {
-        ALOGE("Failed to parse reply message = %d", res);
+        ALOGE("Failed to parse event = %d", res);
         res = NL_SKIP;
     } else {
         res = cmd->handleEvent(event);
diff --git a/service/lib/cpp_bindings.h b/service/lib/cpp_bindings.h
index 525742c..45db5db 100644
--- a/service/lib/cpp_bindings.h
+++ b/service/lib/cpp_bindings.h
@@ -15,11 +15,15 @@
 public:
     WifiEvent(nl_msg *msg) {
         mMsg = msg;
+        mHeader = NULL;
+        memset(mAttributes, 0, sizeof(mAttributes));
     }
     ~WifiEvent() {
         /* don't destroy mMsg; it doesn't belong to us */
     }
 
+    void log();
+
     int parse();
 
     genlmsghdr *header() {
@@ -30,6 +34,8 @@
         return mHeader->cmd;
     }
 
+    const char *get_cmdString();
+
     nlattr ** attributes() {
         return mAttributes;
     }
diff --git a/service/lib/gscan.cpp b/service/lib/gscan.cpp
index f6ce57b..3b2eeca 100644
--- a/service/lib/gscan.cpp
+++ b/service/lib/gscan.cpp
@@ -71,47 +71,67 @@
 class ScanCommand : public WifiCommand
 {
 private:
-    static const unsigned int MAX_CHANNELS = 64;
-    int mChannels[MAX_CHANNELS];
-    int mNumChannels;
-    IScanResultsHandler mHandler;
-    static const unsigned int MAX_RESULTS = 64;
+    static const unsigned int MAX_BUCKETS = 8;
+    wifi_scan_bucket_spec mBuckets[MAX_BUCKETS];
+    int mNumBuckets;
+    wifi_scan_result_handler mHandler;
+    static const unsigned int MAX_RESULTS = 1024;
     wifi_scan_result mResults[MAX_RESULTS];
 public:
-    ScanCommand(wifi_handle handle, int id, int *channels, unsigned n,
-                IScanResultsHandler handler)
+    ScanCommand(wifi_handle handle, int id, wifi_scan_bucket_spec *buckets, unsigned n,
+                wifi_scan_result_handler handler)
         : WifiCommand(handle, id), mHandler(handler)
     {
-        mNumChannels = n > MAX_CHANNELS ? MAX_CHANNELS : n;
-        for (int i = 0; i < mNumChannels; i++) {
-            mChannels[i] = channels[i];
+        mNumBuckets = n > MAX_BUCKETS ? MAX_BUCKETS : n;
+        for (int i = 0; i < mNumBuckets; i++) {
+            mBuckets[i] = buckets[i];
         }
     }
 
     virtual int create() {
-        int ret = mMsg.create(NL80211_CMD_START_SCHED_SCAN, 0, 0);
+        ALOGD("Creating message to scan");
+
+        int ret = mMsg.create(NL80211_CMD_TRIGGER_SCAN, 0, 0);
         if (ret < 0) {
             return ret;
         }
+
+        mMsg.put_u32(NL80211_ATTR_IFINDEX, 0);
+
         struct nlattr * attr = mMsg.attr_start(NL80211_ATTR_SCAN_FREQUENCIES);
-        for (int i = 0; i < mNumChannels; i++) {
-            ret = mMsg.put_u32(i + 1, mChannels[i]);
-            if (ret < 0) {
-                return ret;
+        int channel_id = 0;
+        for (int i = 0; i < mNumBuckets; i++) {
+            wifi_scan_bucket_spec &bucket = mBuckets[i];
+            for (int j = 0; j < bucket.num_channels; j++) {
+                ret = mMsg.put_u32(channel_id++, bucket.channels[j].channel);
+                if (ret < 0) {
+                    return ret;
+                }
             }
         }
+
         mMsg.attr_end(attr);
-        mMsg.put_u32(NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_FLUSH);
+        // mMsg.put_u32(NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_FLUSH);
         return ret;
     }
 
     int start() {
-        return requestEvent(NL80211_CMD_SCHED_SCAN_RESULTS);
+        registerHandler(NL80211_CMD_NEW_SCAN_RESULTS);
+
+        ALOGD("Requesting events to scheduled scan");
+        int result = requestResponse();
+
+        if (result != WIFI_SUCCESS) {
+            ALOGD("failed to start scan; result = %d", result);
+        }
+
+        return result;
     }
 
     virtual int cancel() {
         /* TODO: send another command to the driver to cancel the scan */
-        wifi_unregister_handler(mInfo, NL80211_CMD_SCHED_SCAN_RESULTS);
+        ALOGD("Cancelling scheduled scan");
+        unregisterHandler(NL80211_CMD_NEW_SCAN_RESULTS);
         return WIFI_SUCCESS;
     }
 
@@ -123,16 +143,38 @@
     virtual int handleEvent(WifiEvent event) {
         ALOGI("Got a scan results event");
 
-        int rem = 0, i = 0;
+        event.log();
 
+        nlattr **attributes = event.attributes();
+        for (int i = 0; i < NL80211_ATTR_MAX; i++) {
+            nlattr *attr = event.get_attribute(i);
+            if (attr != NULL) {
+                ALOGI("Found attribute : %d", i);
+            }
+        }
+
+        nlattr *attr = event.get_attribute(NL80211_ATTR_SCAN_SSIDS);
+        if (event.get_attribute(NL80211_ATTR_SCAN_SSIDS) == NULL) {
+            ALOGI("No SSIDs found");
+            return NL_SKIP;
+        }
+
+        ALOGI("SSID attribute size = %d", event.len(NL80211_ATTR_SCAN_SSIDS));
+
+        int rem = 0, i = 0;
         nl_iterator it(event.get_attribute(NL80211_ATTR_SCAN_SSIDS));
-        for ( ; it.has_next(); it.next()) {
+        for ( ; it.has_next(); it.next(), i++) {
             struct nlattr *attr = it.get();
             wifi_scan_result *result = &mResults[i];
             char *ssid = (char *)nla_data(attr);
             int len = nla_len(attr);
-            memcpy(result->ssid, ssid, len);
-            ssid[len] = 0;
+            if (len < (int)sizeof(result->ssid)) {
+                memcpy(result->ssid, ssid, len);
+                result->ssid[len] = 0;
+                ALOGI("Found SSID : len = %d, value = %s", len, result->ssid);
+            } else {
+                ALOGI("Ignroed SSID : len = %d", len);
+            }
         }
 
         (*mHandler.on_scan_results)(id(), i, mResults);
@@ -144,19 +186,22 @@
         wifi_request_id id,
         wifi_interface_handle iface,
         wifi_scan_cmd_params params,
-        IScanResultsHandler handler)
+        wifi_scan_result_handler handler)
 {
     interface_info *iinfo = (interface_info *)iface;
     wifi_handle handle = iinfo->handle;
     hal_info *info = (hal_info *)handle;
 
-    ScanCommand *cmd = new ScanCommand(handle, id, params.channels, params.num_channels, handler);
+    ALOGD("Starting GScan, halHandle = %p", handle);
+
+    ScanCommand *cmd = new ScanCommand(handle, id, params.buckets, params.num_buckets, handler);
     wifi_register_cmd(handle, id, cmd);
     return (wifi_error)cmd->start();
 }
 
 wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface)
 {
+    ALOGD("Stopping GScan");
     interface_info *iinfo = (interface_info *)iface;
     wifi_handle handle = iinfo->handle;
     hal_info *info = (hal_info *)handle;
diff --git a/service/lib/gscan.h b/service/lib/gscan.h
index 43da69e..564e8f3 100644
--- a/service/lib/gscan.h
+++ b/service/lib/gscan.h
@@ -6,11 +6,36 @@
 
 /* AP Scans */
 
+typedef enum {
+    WIFI_BAND_UNSPECIFIED,
+    WIFI_BAND_BG,                       // 2.4 GHz
+    WIFI_BAND_A,                        // 5 GHz without DFS
+    WIFI_BAND_A_WITH_DFS,               // 5 GHz with DFS
+    WIFI_BAND_ABG,                      // 2.4 GHz + 5 GHz; no DFS
+    WIFI_BAND_ABG_WITH_DFS,             // 2.4 GHz + 5 GHz with DFS
+} wifi_band;
+
+wifi_error wifi_get_valid_channels(wifi_handle handle,
+        int band, int size, wifi_channel *channels, int *num);
+
+typedef struct {
+    int max_scan_cache_size;                 // in number of scan results??
+    int max_scan_buckets;
+    int max_ap_cache_per_scan;
+    int max_rssi_sample_size;
+    int max_scan_reporting_threshold;        // in number of scan results??
+    int max_hotlist_aps;
+    int max_significant_wifi_change_aps;
+} wifi_gscan_capabilities;
+
+wifi_error wifi_get_gscan_capabilities(wifi_handle handle,
+        wifi_gscan_capabilities *capabilities);
+
 typedef struct {
     wifi_timestamp ts;                  // Time of discovery
-    char ssid[32];                      // May not be null terminated
+    char ssid[32+1];                    // null terminated
     mac_addr bssid;
-    wifi_channel channel;               // channel number (includes all bands)
+    wifi_channel channel;               // channel frequency in MHz
     wifi_rssi rssi;                     // in db
     wifi_timespan rtt;                  // in nanoseconds
     wifi_timespan rtt_sd;               // standard deviation in rtt
@@ -20,44 +45,79 @@
 
 typedef struct {
     void (*on_scan_results) (wifi_request_id id, unsigned num_results, wifi_scan_result *results);
-} IScanResultsHandler;
+} wifi_scan_result_handler;
 
 typedef struct {
+    wifi_channel channel;               // frequency
+    int dwellTimeMs;                    // dwell time hint
+    int passive;                        // 0 => active, 1 => passive scan; ignored for DFS
+    /* Add channel class */
+} wifi_scan_channel_spec;
+
+typedef struct {
+    int bucket;                         // bucket index, 0 based
+    wifi_band band;                     // when UNSPECIFIED, use channel list
     int num_channels;
-    wifi_channel channels[];            // channels to scan; these may include DFS channels
-    int single_shot;                    // boolean, 0 => repeated, 1 => single
-    int frequency;                      // desired frequency, in scans per minute; if this is too
-                                        // high, the firmware should choose to generate results as
+    wifi_scan_channel_spec channels[8]; // channels to scan; these may include DFS channels
+    int period;                         // desired period, in millisecond; if this is too
+                                        // low, the firmware should choose to generate results as
                                         // fast as it can instead of failing the command
+    byte report_events;                 // 1 => report events after each scan
+
+} wifi_scan_bucket_spec;
+
+typedef struct {
+    int period;                         // base timer period
+    int num_buckets;                    // maximum 8
+    wifi_scan_bucket_spec buckets[];
+    int max_ap_per_scan;
+    int report_threshold;               // in %, when buffer is this much full, wake up AP
 } wifi_scan_cmd_params;
 
 wifi_error wifi_start_gscan(wifi_request_id id, wifi_interface_handle iface,
-        wifi_scan_cmd_params params, IScanResultsHandler handler);
+        wifi_scan_cmd_params params, wifi_scan_result_handler handler);
 wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface);
 
-/*
- * Expect multiple scans to be active at the same time; the firmware is free to schedule scans
- * as it sees fit; as long as frequency requirements are met. This will allow us to have separate
- * schedules for DFS scans versus 1/6/11 scans.
- * If any channel is supplied multiple times, then the last specification wins.
- */
+wifi_error wifi_get_cached_results(wifi_interface_handle iface, byte flush,
+        wifi_scan_result_handler handler);
 
-/* Background scan - it works with above API with single_shot set to '0' */
 
 /* BSSID Hotlist */
 typedef struct {
-    void (*on_hotlist_ap_found)(wifi_request_id id, unsigned num_results, wifi_scan_result *results);
+    void (*on_hotlist_ap_found)(wifi_request_id id,
+            unsigned num_results, wifi_scan_result *results);
 } wifi_hotlist_ap_found_handler;
 
+typedef struct {
+    mac_addr  bssid;                    // AP BSSID
+    wifi_rssi low;                      // low threshold
+    wifi_rssi high;                     // high threshold
+} ap_threshold_param;
+
+typedef struct {
+    int num;                            // max??
+    ap_threshold_param bssids[];
+} wifi_bssid_hotlist_params;
+
 wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface,
-        int num_bssid, mac_addr bssid[], wifi_hotlist_ap_found_handler handler);
+        wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler);
 wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface);
 
 /* Significant wifi change*/
+
 typedef struct {
-    void (*on_significant_change)(wifi_request_id id, unsigned num_results, wifi_scan_result *results);
+    void (*on_significant_change)(wifi_request_id id,
+            unsigned num_results, wifi_scan_result *results);
 } wifi_significant_change_handler;
 
+typedef struct {
+    int rssi_sample_size;               // number of samples for averaging RSSI
+    int lost_ap_sample_size;            // number of samples to confirm AP loss
+    int min_breaching;                  // number of APs breaching threshold
+    int num;                            // max??
+    ap_threshold_param bssids[];
+} wifi_significant_change_params;
+
 wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface,
         wifi_significant_change_handler handler);
 wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface);
diff --git a/service/lib/wifi_hal.cpp b/service/lib/wifi_hal.cpp
index ad4650d..8b19a55 100644
--- a/service/lib/wifi_hal.cpp
+++ b/service/lib/wifi_hal.cpp
@@ -81,7 +81,7 @@
     }
 
     // ALOGI("Making socket nonblocking");
-	if (nl_socket_set_nonblocking(sock)) {
+    if (nl_socket_set_nonblocking(sock)) {
         ALOGE("Could make socket non-blocking");
         nl_socket_free(sock);
         return NULL;
@@ -252,9 +252,8 @@
     do {
         int timeout = -1;                   /* Infinite timeout */
         pfd.revents = 0;
-        ALOGI("Polling socket");
+        //ALOGI("Polling socket");
         int result = poll(&pfd, 1, -1);
-        ALOGI("Poll result = %0x", result);
         if (result < 0) {
             ALOGE("Error polling socket");
         } else if (pfd.revents & (POLLIN | POLLHUP | POLLERR)) {
@@ -281,29 +280,35 @@
         return NL_SKIP;
     }
 
-	int cmd = event.get_cmd();
-	uint32_t vendor_id = 0;
+    int cmd = event.get_cmd();
+    uint32_t vendor_id = 0;
+    uint32_t subcmd = 0;
 
-	if (cmd == NL80211_CMD_VENDOR) {
+    if (cmd == NL80211_CMD_VENDOR) {
         vendor_id = event.get_u32(NL80211_ATTR_VENDOR_ID);
-        // subcmd = event.get_u32(NL80211_ATTR_VENDOR_SUBCMD);
-	}
+        subcmd = event.get_u32(NL80211_ATTR_VENDOR_SUBCMD);
+        ALOGI("event received %s, vendor_id = 0x%0x, subcmd = 0x%0x",
+                event.get_cmdString(), vendor_id, subcmd);
+    } else {
+        ALOGI("event received %s", event.get_cmdString());
+    }
 
-	ALOGI("event received %d, vendor_id = 0x%0x", cmd, vendor_id);
+    ALOGI("event received %s, vendor_id = 0x%0x", event.get_cmdString(), vendor_id);
+    event.log();
 
-	for (int i = 0; i < info->num_event_cb; i++) {
-	    if (cmd == info->event_cb[i].nl_cmd) {
-	        if (cmd == NL80211_CMD_VENDOR && vendor_id != info->event_cb[i].vendor_id) {
-	            /* event for a different vendor, ignore it */
-	            continue;
-	        }
+    for (int i = 0; i < info->num_event_cb; i++) {
+        if (cmd == info->event_cb[i].nl_cmd) {
+            if (cmd == NL80211_CMD_VENDOR && vendor_id != info->event_cb[i].vendor_id) {
+                /* event for a different vendor, ignore it */
+                continue;
+            }
 
-	        cb_info *cbi = &(info->event_cb[i]);
-	        return (*(cbi->cb_func))(msg, cbi->cb_arg);
-	    }
-	}
+            cb_info *cbi = &(info->event_cb[i]);
+            return (*(cbi->cb_func))(msg, cbi->cb_arg);
+        }
+    }
 
-	return NL_OK;
+    return NL_OK;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////
@@ -447,6 +452,7 @@
                 free(ifinfo);
                 continue;
             }
+            ifinfo->handle = handle;
             info->interfaces[i] = ifinfo;
             i++;
         }
diff --git a/service/lib/wifi_hal.h b/service/lib/wifi_hal.h
index 5852528..85a46a3 100644
--- a/service/lib/wifi_hal.h
+++ b/service/lib/wifi_hal.h
@@ -23,7 +23,7 @@
 typedef uint64_t u64;
 typedef void * wifi_handle;
 typedef int wifi_request_id;
-typedef int wifi_channel;
+typedef int wifi_channel;                       // indicates channel frequency in MHz
 typedef int wifi_rssi;
 typedef byte mac_addr[6];
 typedef int64_t wifi_timestamp;                 // In microseconds (us)
diff --git a/service/tools/halutil/halutil.cpp b/service/tools/halutil/halutil.cpp
new file mode 100644
index 0000000..70a2a21
--- /dev/null
+++ b/service/tools/halutil/halutil.cpp
@@ -0,0 +1,174 @@
+
+#include <stdlib.h>
+#include <pthread.h>
+
+#include "wifi_hal.h"
+
+#define LOG_TAG  "WifiHAL"
+
+#include <utils/Log.h>
+#include <inttypes.h>
+
+#define EVENT_BUF_SIZE 2048
+
+static wifi_handle halHandle;
+static wifi_interface_handle *ifaceHandles;
+static int numIfaceHandles;
+static int cmdId = 0;
+
+static int init() {
+    wifi_error res = wifi_initialize(&halHandle);
+    if (res < 0) {
+        return res;
+    }
+
+    res = wifi_get_ifaces(halHandle, &numIfaceHandles, &ifaceHandles);
+    if (res < 0) {
+        return res;
+    }
+
+    char buf[EVENT_BUF_SIZE];
+    for (int i = 0; i < numIfaceHandles; i++) {
+        if (wifi_get_iface_name(ifaceHandles[i], buf, sizeof(buf)) == WIFI_SUCCESS) {
+            printf("found interface %s\n", buf);
+        }
+    }
+
+    return res;
+}
+
+static void cleaned_up_handler(wifi_handle handle) {
+    printf("HAL cleaned up handler\n");
+    halHandle = NULL;
+    ifaceHandles = NULL;
+}
+
+static void cleanup() {
+    printf("cleaning up HAL\n");
+
+    wifi_cleanup(halHandle, cleaned_up_handler);
+
+    /* wait for clean_up_handler to execute */
+    /*
+    while (halHandle != NULL) {
+        sleep(100);
+    }
+    */
+}
+
+static void *eventThreadFunc(void *context) {
+
+    printf("starting wifi event loop\n");
+    wifi_event_loop(halHandle);
+    printf("out of wifi event loop\n");
+
+    return NULL;
+}
+
+
+static int getNewCmdId() {
+    return cmdId++;
+}
+
+/* -------------------------------------------  */
+/* helpers                                      */
+/* -------------------------------------------  */
+
+void printScanResult(wifi_scan_result result) {
+
+    printf("SSID = %s\n", result.ssid);
+
+    printf("BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", result.bssid[0], result.bssid[1],
+            result.bssid[2], result.bssid[3], result.bssid[4], result.bssid[5]);
+
+    printf("RSSI = %d\n", result.rssi);
+    printf("Channel = %d\n", result.channel);
+    printf("timestamp = %lld\n", result.ts);
+    printf("rtt = %lld\n", result.rtt);
+    printf("rtt_sd = %lld\n", result.rtt_sd);
+}
+
+
+/* -------------------------------------------  */
+/* commands and events                          */
+/* -------------------------------------------  */
+
+static void onScanResults(wifi_request_id id, unsigned num_results, wifi_scan_result *results) {
+
+    printf("Received scan results\n");
+
+    for (unsigned i = 0; i < num_results; i++) {
+        printScanResult(results[i]);
+    }
+}
+
+static int scanCmdId;
+
+static bool startScan() {
+
+    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;
+
+    scanCmdId = getNewCmdId();
+
+    return wifi_start_gscan(scanCmdId, ifaceHandles[0], params, handler) == WIFI_SUCCESS;
+}
+
+static void stopScan() {
+    if (scanCmdId != 0) {
+        wifi_stop_gscan(scanCmdId, ifaceHandles[0]);
+        scanCmdId = 0;
+    }
+}
+
+/* -------------------------------------------  */
+/* tests                                        */
+/* -------------------------------------------  */
+
+void testScan() {
+    printf("starting scan\n");
+    if (!startScan()) {
+        printf("failed to start scan!!\n");
+    } else {
+        sleep(2);
+        stopScan();
+        printf("stopped scan\n");
+    }
+}
+
+int main(int argc, char *argv[]) {
+
+    printf("successfully initialized HAL\n");
+
+    if (init() != 0) {
+        printf("could not initiate HAL");
+        return -1;
+    } else {
+        printf("successfully initialized HAL\n");
+    }
+
+    pthread_t tid;
+    pthread_create(&tid, NULL, &eventThreadFunc, NULL);
+
+    sleep(2);     // let the thread start
+
+    if (argc == 2) {
+        if (argv[1][0] != '-') {
+            printf("%s: invalid usage", argv[0]);
+            goto cleanup;
+        }
+
+        if (argv[1][1] == 's') {
+            testScan();
+        }
+    }
+
+cleanup:
+    cleanup();
+    // pthread_join(tid, NULL);
+    return 0;
+}