Implement com.dsi.ant hidl client.

Changes the hal service to be a hidl client instead of using
libantradio.so. This is part of the changes needed to support
project treble.

Some config variables have also been moved into an xml
resource. This allows vendors to customize behaviour using an
overlay rather than modifying the hal service source.
diff --git a/Android.mk b/Android.mk
index 2169f06..060fd24 100644
--- a/Android.mk
+++ b/Android.mk
@@ -16,7 +16,6 @@
 # limitations under the License.
 #
 
-ifneq ($(BOARD_ANT_WIRELESS_DEVICE),)
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -29,12 +28,17 @@
     src/com/dsi/ant/server/IAntHal.aidl \
     src/com/dsi/ant/server/IAntHalCallback.aidl
 
-LOCAL_REQUIRED_MODULES := libantradio
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    com.dsi.ant-V1.0-java-static
+
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_CERTIFICATE := platform
 LOCAL_MODULE_TAGS := optional
 LOCAL_PACKAGE_NAME := AntHalService
 
-include $(BUILD_PACKAGE)
+# Remove all verbose and debug logging when building user.
+ifeq ($(TARGET_BUILD_VARIANT),user)
+    LOCAL_PROGUARD_FLAG_FILES += proguard.nolog.flags
+endif
 
-endif # BOARD_ANT_WIRELESS_DEVICE defined
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2d1a25b..1bc42d8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.dsi.ant.server"
-    android:versionName="4.1.1"
-    android:versionCode="040101"
+    android:versionName="5.0.0_hidl_1"
+    android:versionCode="050000"
     android:sharedUserId="android.uid.system">
 
     <uses-sdk
diff --git a/proguard.flags b/proguard.flags
index 965f85d..ddcfce3 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -18,14 +18,8 @@
 #
 ###############################################################################
 
--keep class com.dsi.ant.server.IAntHal
+# Need to manually keep any entrance points that are only referenced by the manifest.
+# Currently this is our exported service and the boot completed receiver.
+-keep class * extends android.app.service
+-keep class * extends android.content.BroadcastReceiver
 
--keep class com.dsi.ant.core.JAntJava {
-    void nativeCb_AntRxMessage(byte[]);
-    void nativeCb_AntStateChange(int);
-}
-
--assumenosideeffects class android.util.Log {
-    public static int v(...);
-    public static int d(...);
-}
diff --git a/proguard.nolog.flags b/proguard.nolog.flags
new file mode 100644
index 0000000..452642f
--- /dev/null
+++ b/proguard.nolog.flags
@@ -0,0 +1,25 @@
+###############################################################################
+#
+# ANT Stack
+#
+# Copyright 2018 Dynastream Innovations
+#
+# 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.
+#
+###############################################################################
+
+# Strip out any verbose/debug logs.
+-assumenosideeffects class android.util.log {
+    public static int v(...);
+    public static int d(...);
+}
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..4136914
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ANT Android Host Stack
+
+ Copyright 2018 Dynastream Innovations
+
+ 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.
+-->
+<resources>
+
+    <!--
+    This flag determines if background users are allowed to use the ANT radio or not. Note that
+    even if this flag is set, the active foreground user always has priority for using the
+    ANT radio.
+    -->
+    <bool name="allow_background_usage">true</bool>
+    <bool name="requires_bluetooth_on">false</bool>
+</resources>
diff --git a/sepolicy/ANTHALService.te b/sepolicy/ANTHALService.te
new file mode 100644
index 0000000..f84d91f
--- /dev/null
+++ b/sepolicy/ANTHALService.te
@@ -0,0 +1,4 @@
+# Currently ANTHALService runs as part of the system server, so we just need
+# to allow the system server to be a client of the ant hal.
+hal_client_domain(system_server, hal_dsi_ant);
+
diff --git a/sepolicy/README.txt b/sepolicy/README.txt
new file mode 100644
index 0000000..0d81a0b
--- /dev/null
+++ b/sepolicy/README.txt
@@ -0,0 +1,6 @@
+The policy files in this directory need to be merged with the platform private
+policy on images that include the ANTHALService apk.
+
+This is either the policy in system/sepolicy/private or in the directory referred
+to by the BOARD_PLAT_PRIVATE_SEPOLICY build variable.
+
diff --git a/src/com/dsi/ant/core/IJAntEnum.java b/src/com/dsi/ant/core/IJAntEnum.java
deleted file mode 100644
index 3a1a825..0000000
--- a/src/com/dsi/ant/core/IJAntEnum.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * ANT Stack
- *
- * Copyright 2009 Dynastream Innovations
- * 
- * 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.
- */
-package com.dsi.ant.core;
-
-public interface IJAntEnum<V> {
-	V getValue();
-}
-
diff --git a/src/com/dsi/ant/core/JAntJava.java b/src/com/dsi/ant/core/JAntJava.java
deleted file mode 100644
index 2229418..0000000
--- a/src/com/dsi/ant/core/JAntJava.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * ANT Stack
- *
- * Copyright 2009 Dynastream Innovations
- *
- * 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.
- */
-package com.dsi.ant.core;
-
-import android.util.Log;
-
-import com.dsi.ant.server.AntHalDefine;
-
-
-/** Class for providing connection with JANTNative.cpp module */
-
-public class JAntJava
-{
-    private static final boolean debug = false;
-    private static final String TAG = "JAntJava";
-    private static ICallback mCallback = null;
-
-
-    /** Static constructor */
-    static
-    {
-        try
-        {
-            System.loadLibrary("antradio");
-        }
-        catch (Exception e)
-        {
-            Log.e("JANT", "Exception during nativeJANT_ClassInitNative (" + e.toString() + ")");
-        }
-    }
-
-    public interface ICallback
-    {
-        void ANTRxMessage(byte[] RxMessage);
-        void ANTStateChange(int NewState);
-    }
-    /*******************************************************************************
-     *
-     * Class Methods
-     *
-     *******************************************************************************/
-
-    public JAntStatus create(ICallback callback)
-    {
-        JAntStatus jAntStatus;
-
-        try
-        {
-            if (debug)
-                Log.d(TAG, "Calling nativeJAnt_Create");
-            int AntStatus = nativeJAnt_Create();
-            jAntStatus = JAntUtils.getEnumConst(JAntStatus.class, AntStatus);
-
-            // Record the caller's callback if create was successful
-            if (JAntStatus.SUCCESS == jAntStatus)
-            {
-                if (debug)
-                    Log.d(TAG, "create: nativeJAnt_Create returned success");
-                mCallback = callback;
-            }
-            else
-            {
-                Log.e(TAG, "create: nativeJAnt_Create failed " + jAntStatus);
-            }
-        }
-        catch(Exception e)
-        {
-            Log.e(TAG, "create: exception during nativeJAnt_Create (" + e.toString() + ")");
-            jAntStatus = JAntStatus.FAILED;
-        }
-
-        return jAntStatus;
-    }
-
-    public JAntStatus destroy()
-    {
-        if (debug)
-            Log.d(TAG, "destroy: entered");
-        JAntStatus jAntStatus;
-
-        try
-        {
-            int AntStatus = nativeJAnt_Destroy();
-            jAntStatus = JAntUtils.getEnumConst(JAntStatus.class, AntStatus);
-            if (JAntStatus.SUCCESS == jAntStatus)
-            {
-                if (debug)
-                    Log.d(TAG, "destroy: nativeJAnt_Destroy returned success");
-                mCallback = null;
-            }
-            else
-            {
-                Log.e(TAG, "destroy: nativeJAnt_Destroy failed " + jAntStatus);
-            }
-        }
-        catch (Exception e)
-        {
-            Log.e(TAG, "destroy: exception during nativeJAnt_Destroy (" + e.toString() + ")");
-            jAntStatus = JAntStatus.FAILED;
-        }
-
-        if (debug)
-            Log.d(TAG, "destroy: exiting");
-
-        return jAntStatus;
-    }
-
-    public JAntStatus enable()
-    {
-        if (debug)
-            Log.d(TAG, "enable: entered");
-
-        JAntStatus jAntStatus;
-
-        try
-        {
-            int AntStatus = nativeJAnt_Enable();
-            jAntStatus = JAntUtils.getEnumConst(JAntStatus.class, AntStatus);
-            if (debug)
-                Log.d(TAG, "After nativeJAnt_Enable, status = " + jAntStatus.toString());
-        }
-        catch (Exception e)
-        {
-            Log.e(TAG, "enable: exception during nativeJAnt_enable (" + e.toString() + ")");
-            jAntStatus = JAntStatus.FAILED;
-        }
-
-        if (debug)
-            Log.d(TAG, "enable: exiting");
-
-        return jAntStatus;
-    }
-
-
-    public JAntStatus disable()
-    {
-        if (debug)
-            Log.d(TAG, "disable: entered");
-
-        JAntStatus jAntStatus;
-
-        try
-        {
-            int status = nativeJAnt_Disable();
-            jAntStatus = JAntUtils.getEnumConst(JAntStatus.class, status);
-            if (debug)
-                Log.d(TAG, "After nativeJAnt_Disable, status = " + jAntStatus.toString());
-        }
-        catch (Exception e)
-        {
-            Log.e(TAG, "disable: exception during nativeJAnt_Disable (" + e.toString() + ")");
-            jAntStatus = JAntStatus.FAILED;
-        }
-
-        if (debug)
-            Log.d(TAG, "disable: exiting");
-
-        return jAntStatus;
-    }
-
-    public int getRadioEnabledStatus()
-    {
-        if (debug) Log.d(TAG, "getRadioEnabledStatus: entered");
-        int retStatus;
-        try
-        {
-            retStatus = nativeJAnt_GetRadioEnabledStatus();
-            if (debug) Log.i(TAG, "Got ANT status as " + retStatus);
-        }
-        catch (Exception e)
-        {
-            Log.e(TAG, "getRadioEnabledStatus: exception during call (" + e.toString() + ")");
-            retStatus = AntHalDefine.ANT_HAL_STATE_UNKNOWN;
-        }
-        if (debug) Log.d(TAG, "getRadioEnabledStatus: exiting");
-        return retStatus;
-    }
-
-    public JAntStatus ANTTxMessage(byte[] message)
-    {
-        if (debug)
-            Log.d(TAG, "ANTTxMessage: entered");
-        JAntStatus jAntStatus;
-
-        try
-        {
-            int AntStatus = nativeJAnt_TxMessage(message);
-            jAntStatus = JAntUtils.getEnumConst(JAntStatus.class, AntStatus);
-            if (debug)
-                Log.d(TAG, "After nativeJAnt_ANTTxMessage, status = " + jAntStatus.toString());
-        }
-        catch (Exception e)
-        {
-            Log.e(TAG, "ANTTxMessage: exception during nativeJAnt_ANTTxMessage (" + e.toString() + ")");
-            jAntStatus = JAntStatus.FAILED;
-        }
-
-        if (debug)
-            Log.d(TAG, "ANTTxMessage: exiting");
-        return jAntStatus;
-    }
-
-    public JAntStatus hardReset()
-    {
-        if (debug)
-            Log.d(TAG, "hardReset: entered");
-
-        JAntStatus jAntStatus;
-
-        try
-        {
-            int status = nativeJAnt_HardReset();
-            jAntStatus = JAntUtils.getEnumConst(JAntStatus.class, status);
-            if (debug)
-                Log.d(TAG, "After nativeJAnt_HardReset, status = " + jAntStatus.toString());
-        }
-        catch (Exception e)
-        {
-            Log.e(TAG, "hardReset: exception during nativeJAnt_HardReset, (" + e.toString() + ")");
-            jAntStatus = JAntStatus.FAILED;
-        }
-
-        if (debug)
-            Log.d(TAG, "hardReset:: exiting");
-
-        return jAntStatus;
-    }
-
-    /* --------------------------------
-     *            NATIVE PART
-     * --------------------------------
-     */
-    /* RBTL Calls */
-    private static native int    nativeJAnt_Create();
-    private static native int    nativeJAnt_Destroy();
-    private static native int    nativeJAnt_Enable();
-    private static native int    nativeJAnt_Disable();
-    private static native int    nativeJAnt_GetRadioEnabledStatus();
-    private static native int    nativeJAnt_TxMessage(byte[] message);
-    private static native int    nativeJAnt_HardReset();
-
-
-    /*    ----------------------------------------------
-     *    Callbacks from the JAntNative.cpp module
-     *    ----------------------------------------------
-     */
-    public static void nativeCb_AntRxMessage(byte[] RxMessage)
-    {
-        if (debug)
-            Log.d(TAG, "nativeCb_AntRxMessage: calling callback");
-
-        if (mCallback != null)
-        {
-            mCallback.ANTRxMessage(RxMessage);
-        }
-        else
-        {
-            Log.e(TAG, "nativeCb_AntRxMessage: callback is null");
-        }
-    }
-
-    public static void nativeCb_AntStateChange(int NewState)
-    {
-        if (debug)
-            Log.d(TAG, "nativeCb_AntStateChange: calling callback");
-
-        if (mCallback != null)
-        {
-            mCallback.ANTStateChange(NewState);
-        }
-        else
-        {
-            Log.e(TAG, "nativeCb_AntStateChange: callback is null");
-        }
-    }
-
-} /* End class */
-
diff --git a/src/com/dsi/ant/core/JAntStatus.java b/src/com/dsi/ant/core/JAntStatus.java
deleted file mode 100644
index 7ee6c43..0000000
--- a/src/com/dsi/ant/core/JAntStatus.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * ANT Stack
- *
- * Copyright 2009 Dynastream Innovations
- * 
- * 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.
- */
-package com.dsi.ant.core;
-
-public enum JAntStatus implements IJAntEnum<Integer> {
-
-SUCCESS                                 (0),
-FAILED                                  (1),
-PENDING                                 (2),
-INVALID_PARM                            (3),
-IN_PROGRESS                             (4),
-NOT_APPLICABLE                          (5),
-NOT_SUPPORTED                           (6),
-INTERNAL_ERROR                          (7),
-TRANSPORT_INIT_ERR                      (8),
-HARDWARE_ERR                            (9),
-NO_VALUE_AVAILABLE                      (10),
-CONTEXT_DOESNT_EXIST                    (11),
-CONTEXT_NOT_DESTROYED                   (12),
-CONTEXT_NOT_ENABLED                     (13),
-CONTEXT_NOT_DISABLED                    (14),
-NOT_DE_INITIALIZED                      (15),
-NOT_INITIALIZED                         (16),
-TOO_MANY_PENDING_CMDS                   (17),
-DISABLING_IN_PROGRESS                   (18),
-COMMAND_WRITE_FAILED                    (20),
-SCRIPT_EXEC_FAILED                      (21),
-
-FAILED_BT_NOT_INITIALIZED               (23),
-AUDIO_OPERATION_UNAVAILIBLE_RESOURCES   (24),
-
-NO_VALUE                                (100);
-
-    private final int value;
-
-    private JAntStatus(int value) 
-    {
-        this.value = value;
-    }
-
-    public Integer getValue() 
-    {
-        return value;
-    }
-}
-
diff --git a/src/com/dsi/ant/core/JAntUtils.java b/src/com/dsi/ant/core/JAntUtils.java
deleted file mode 100644
index a9ff0ac..0000000
--- a/src/com/dsi/ant/core/JAntUtils.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * ANT Stack
- *
- * Copyright 2009 Dynastream Innovations
- * 
- * 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.
- */
-package com.dsi.ant.core;
-
-import java.lang.Enum;
-import java.util.EnumSet;
-
-public final class JAntUtils {
-
-	public static <V, E extends Enum<E> & IJAntEnum<V>> E getEnumConst(Class<E> enumType, V constValue) {		
-		EnumSet<E> es = EnumSet.allOf(enumType);
-
-		E  matchingConst = null;
-
-		for (E enumConst: es) {
-			 if (enumConst.getValue().equals(constValue))
-			 {
-				 matchingConst = enumConst;
-				 break;
-			 }
-		}		
-		
-		if (matchingConst == null) {
-			//Log.e(TAG, "getEnumConst: Invalid const (" + constValue + ") for " + enumType.toString());
-		}
-		return matchingConst;
-	}	
-}
-
diff --git a/src/com/dsi/ant/hidl/HidlClient.java b/src/com/dsi/ant/hidl/HidlClient.java
new file mode 100644
index 0000000..9782903
--- /dev/null
+++ b/src/com/dsi/ant/hidl/HidlClient.java
@@ -0,0 +1,697 @@
+/*
+ * ANT Android Host Stack
+ *
+ * Copyright 2018 Dynastream Innovations
+ *
+ * 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.
+ */
+
+package com.dsi.ant.hidl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+
+import com.dsi.ant.V1_0.IAnt;
+import com.dsi.ant.V1_0.IAntCallbacks;
+import com.dsi.ant.V1_0.ImplProps;
+import com.dsi.ant.V1_0.OptionFlags;
+import com.dsi.ant.server.AntHalDefine;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IHwBinder.DeathRecipient;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Class for interacting over the HIDL interface to the hal server.
+ */
+public final class HidlClient {
+
+    /*
+     * Notes:
+     *
+     * The synchronization model here is to do pretty much everything with the state lock held.
+     * All private functions assume that they are called with the state lock held,
+     * and the public functions generally grab the lock for the duration.
+     *
+     * Exceptions to this are:
+     *  - Code that makes callbacks. Easy to avoid deadlocks if we never call out with locks held.
+     *  - The tx message function. Sending a data message may be blocked but we still need to send control messages.
+     *  - Retrieving the current state. Generally want to be able to do this while power control operations are in progress.
+     *  - Separate lock used for flow controlling.
+     */
+
+    /**
+     * Callbacks made by this client into the code in AntServce.java
+     */
+    public interface ICallback {
+        void ANTRxMessage(byte[] RxMessage);
+
+        /** State values are AntHalDefine.ANT_HAL_STATE_ constants. */
+        void ANTStateChange(int NewState);
+    }
+
+    /** Logcat tag. */
+    private static final String TAG = "ANT HidlClient";
+
+    // For keepalive use the invalid message id 0.
+    private static final byte[] KEEPALIVE_MESG = new byte[] {(byte)0x01, (byte)0x00, (byte)0x00};
+    // Response is an invalid message id response.
+    private static final byte[] KEEPALIVE_RESP = new byte[] {(byte)0x03, (byte)0x40, (byte)0x00, (byte)0x00, (byte)0x28};
+
+
+    // Constants for the keep alive handler
+    /** Start the keep alive process */
+    private static final int KEEPALIVE_START = 0;
+    /** Stop the keep alive process */
+    private static final int KEEPALIVE_STOP = 1;
+    /** Reset the keep alive timer */
+    private static final int KEEPALIVE_RESET = 2;
+    /** Internal to keep alive handler, indicates the initial timeout. */
+    private static final int KEEPALIVE_PING = 3;
+    /** Internal to keep alive handler, indicates that no response was received. */
+    private static final int KEEPALIVE_TIMEOUT = 4;
+
+    /** Value for mDeathCookie indicating we have no valid alive interface right now. */
+    private static final long INVALID_DEATH_COOKIE = 0L;
+
+    /** Timeout for flow control responses. */
+    private static final long FLOW_CONTROL_TIMEOUT_NS = 10L * 1000L * 1000L * 1000L;
+
+    /** Timeout for keepalive */
+    private static final long KEEPALIVE_TIMEOUT_MS = 5L * 1000L;
+
+    // Constants for server bind retries.
+    private static final int HAL_BIND_RETRY_DELAY_MS = 100;
+    private static final int HAL_BIND_RETRY_COUNT = 10;
+
+
+    /** Lock object used for all synchronization in this class. */
+    private final Object mStateLock = new Object();
+
+    /** Lock for using flow control on the data channel. */
+    private final Object mFlowControlLock = new Object();
+
+    /** Separate thread for making state change callbacks. */
+    private final Handler mStateDispatcher;
+    {
+        HandlerThread thread = new HandlerThread("State Dispatch");
+        thread.start();
+        mStateDispatcher = new Handler(thread.getLooper());
+    }
+
+    /** Handler for dispatching a recovery attempt from failed message transmits. */
+    private final Handler mRecoveryDispatcher = new Handler();
+
+
+    /** Power state of transport. */
+    private volatile int mState = AntHalDefine.ANT_HAL_STATE_DISABLED;
+
+    /** Callbacks to AntService. */
+    private volatile ICallback mCallbacks;
+
+    /** Handle to HIDL server. */
+    private IAnt mHidl;
+
+    /** Properties of the server implementation. */
+    private ImplProps mHidlProps;
+
+    /** Keep track of the last cookie given to linkToDeath() */
+    private long mDeathCookie = INVALID_DEATH_COOKIE;
+
+    /** Next value to use for the death cookie. */
+    private long mNextDeathCookie = 1;
+
+    /** Whether flow control is used or not. */
+    private boolean mUseFlowControl = false;
+
+    /** Flag to communicate that a flow go was received. */
+    private boolean mFlowGoReceived = false;
+
+    /** Whether keep alive should be used or not. */
+    private boolean mUseKeepalive = false;
+
+
+    /**
+     * Initial setup at ANTHALService startup.
+     */
+    public void create(ICallback callback) {
+        synchronized (mStateLock) {
+            mCallbacks = callback;
+        }
+    }
+
+    /**
+     * Cleanup when ANTHALService is shutting down.
+     */
+    public void destroy() {
+        synchronized (mStateLock) {
+            // No need to send state updates since we only destroy when no one is bound.
+            disable();
+            cleanupHidl();
+        }
+    }
+
+    /**
+     * Enable the transport. Will perform the binding to the HIDL server if required.
+     * @return true if transport bring up was successful, false otherwise.
+     */
+    public boolean enable() {
+        synchronized (mStateLock) {
+
+            // If already enabled there is nothing to do.
+            if (mState == AntHalDefine.ANT_HAL_STATE_ENABLED) {
+                Log.d(TAG, "Ignoring enable(), already enabled.");
+                return true;
+            }
+
+            updateState(AntHalDefine.ANT_HAL_STATE_ENABLING);
+
+            if (doBringup()) {
+                updateState(AntHalDefine.ANT_HAL_STATE_ENABLED);
+                return true;
+            } else {
+                updateState(AntHalDefine.ANT_HAL_STATE_DISABLED);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Disable the transport.
+     */
+    public void disable() {
+        synchronized (mStateLock) {
+
+            // If already disabled there is nothing to do.
+            if (mState == AntHalDefine.ANT_HAL_STATE_DISABLED) {
+                Log.d(TAG, "Ignoring disable(), already disabled.");
+            }
+
+            updateState(AntHalDefine.ANT_HAL_STATE_DISABLING);
+            try {
+                int status = mHidl.disable();
+                if (status != 0) {
+                    Log.w(TAG, "Failed to disable ANT: " + mHidl.translateStatus(status));
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Server died in disable", e);
+                handleServerDeath();
+            }
+
+            updateState(AntHalDefine.ANT_HAL_STATE_DISABLED);
+        }
+    }
+
+    /**
+     * Perform a full power cycle of the transport/chip. Usually used to recover from error conditions.
+     * @return true if the transport/chip where successfully brought back to an operational state.
+     */
+    public boolean hardReset() {
+        synchronized (mStateLock) {
+
+            updateState(AntHalDefine.ANT_HAL_STATE_RESETTING);
+
+            if (doRecovery()) {
+                updateState(AntHalDefine.ANT_HAL_STATE_RESET);
+                return true;
+            } else {
+                updateState(AntHalDefine.ANT_HAL_STATE_DISABLED);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Query the current state of the transport.
+     * @return Ant ANT_HAL_STATE constant representing the current state of the transport.
+     */
+    public int getRadioEnabledStatus() {
+        // No need to hold lock, since value is marked as volatile.
+        return mState;
+    }
+
+    /**
+     * Send a message to the firmware over the transport.
+     * @param message The ANT message to send.
+     * @return True if the message was successfully sent, false otherwise.
+     */
+    public boolean ANTTxMessage(byte[] message) {
+
+        // Cache a few things that might change during the unsynchronized portion.
+        IAnt cachedHidl;
+        long cachedDeathCookie;
+        boolean succeeded = false;
+
+        synchronized (mStateLock) {
+
+            // Hidl interface is only valid while enabled.
+            if (mState != AntHalDefine.ANT_HAL_STATE_ENABLED) {
+                Log.w(TAG, "Failing message tx because transport was not enabled.");
+                return false;
+            }
+
+            cachedHidl = mHidl;
+            cachedDeathCookie = mDeathCookie;
+        }
+
+        // The hidl interface uses ArrayLists, so need to convert.
+        ArrayList<Byte> hidlMessage = new ArrayList<>(message.length);
+        for (byte b : message) {
+            hidlMessage.add(b);
+        }
+
+        // Dispatch to proper channel.
+        try {
+            int status;
+            boolean timeout = false;
+
+            switch (message[Mesg.ID_OFFSET]) {
+            case Mesg.BROADCAST_DATA_ID:
+            case Mesg.ACKNOWLEDGED_DATA_ID:
+            case Mesg.BURST_DATA_ID:
+            case Mesg.EXT_BROADCAST_DATA_ID:
+            case Mesg.EXT_ACKNOWLEDGED_DATA_ID:
+            case Mesg.EXT_BURST_DATA_ID:
+            case Mesg.ADV_BURST_DATA_ID:
+                // Data messages may be flow controlled at protocol level.
+                synchronized (mFlowControlLock) {
+                    status = cachedHidl.sendDataMessage(hidlMessage);
+                    if (mUseFlowControl) {
+                        timeout = !waitForFlowGoLocked();
+                    }
+                }
+                break;
+            default:
+                status = cachedHidl.sendCommandMessage(hidlMessage);
+                break;
+            }
+
+            if (timeout) {
+                Log.e(TAG, "Timeout waiting for flow control response.");
+            } else if (status != 0) {
+                Log.e(TAG, "Failed to send message: " + mHidl.translateStatus(status));
+            } else {
+                succeeded = true;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Server died while sending message.", e);
+            // Treat the interface going down during sending of a message the
+            // same as if an async crash was detected.
+            mRecoveryDispatcher.post(new Runnable() {
+                private long deathCookie = cachedDeathCookie;
+
+                @Override
+                public void run() {
+                    mDeathHandler.serviceDied(deathCookie);
+                }
+            });
+        }
+
+        return succeeded;
+    }
+
+    /**
+     * Wait for a flow go to be received. This should only be called with the flow control
+     * lock held, and the lock should be grabbed before sending the message that prompts the flow go response.
+     * @return True if a flow go message was received within the timeout.
+     */
+    public boolean waitForFlowGoLocked() {
+        mFlowGoReceived = false;
+        long deadline = System.nanoTime() + FLOW_CONTROL_TIMEOUT_NS;
+        long diff = deadline - System.nanoTime();
+
+        while (diff > 0 && !mFlowGoReceived) {
+            try {
+                // Wait takes a time in milliseconds
+                mFlowControlLock.wait(diff / (1000*1000));
+            } catch (InterruptedException e) {
+                // Shouldn't be interrupted, but if it is just move the deadline to now.
+                deadline = System.nanoTime();
+            }
+            diff = deadline - System.nanoTime();
+        }
+
+        return mFlowGoReceived;
+    }
+
+    /**
+     * Make sure that the interface to the hidl server is fully brought up.
+     * @return true if the interface is fully initialized, false otherwise.
+     */
+    private boolean setupHidl() {
+        boolean succeeded = false;
+
+        try {
+            // If the interface is already up there is nothing to do.
+            if (mHidl != null) {
+                return true;
+            }
+
+            // Use a unique cookie for link to death. This filters out extraneous death notifications.
+            mDeathCookie = mNextDeathCookie++;
+            // Shouldn't ever reach wrap around, but just in case...
+            if (mNextDeathCookie == INVALID_DEATH_COOKIE) {
+                mNextDeathCookie++;
+            }
+
+            // Initial setup of the interface.
+            int bindAttempts = 0;
+            while ((mHidl == null) && (bindAttempts < HAL_BIND_RETRY_COUNT)) {
+                if (bindAttempts > 0) {
+                    Log.i(TAG, "Could not find hal. Retrying in a little bit.");
+                    try {
+                        Thread.sleep(HAL_BIND_RETRY_DELAY_MS);
+                    } catch (InterruptedException e) {
+                        break;
+                    }
+                }
+
+                bindAttempts++;
+                try {
+                    mHidl = IAnt.getService();
+                } catch (NoSuchElementException e) {
+                    // Do nothing, mHidl is null and we will delay then retry.
+                }
+            }
+            if (mHidl == null) {
+                Log.e(TAG, "Unable to bind to hidl server.");
+                handleServerDeath();
+            } else {
+                mHidl.linkToDeath(mDeathHandler, mDeathCookie);
+                mHidlProps = mHidl.getProperties();
+                mUseFlowControl = (OptionFlags.USE_ANT_FLOW_CONTROL & mHidlProps.options) != 0;
+                mUseKeepalive = (OptionFlags.USE_KEEPALIVE & mHidlProps.options) != 0;
+                mHidl.setCallbacks(mHidlCallbacks);
+
+                Log.i(TAG, "Successfully bound to HAL server.");
+                Log.d(TAG, "Server properties: " + mHidlProps);
+
+                succeeded = true;
+            }
+
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to bind to hidl server.", e);
+            // Make sure everything is cleaned up.
+            handleServerDeath();
+        }
+
+        return succeeded;
+    }
+
+    /**
+     * Cleanup the binding to the HIDL server.
+     */
+    private void cleanupHidl() {
+
+        // Nothing to do if we don't currently have an interface binding.
+        if (mHidl == null) {
+            return;
+        }
+
+        // This can only be done if the server is still alive.
+        try {
+            mHidl.unlinkToDeath(mDeathHandler);
+            mHidl.setCallbacks(null);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error clearing hidl callbacks", e);
+        }
+
+        // Perform any cleanup for the server being gone.
+        handleServerDeath();
+    }
+
+    /**
+     * Cleanup local resources after the server has gone away.
+     */
+    private void handleServerDeath() {
+        mDeathCookie = INVALID_DEATH_COOKIE;
+        mHidl = null;
+    }
+
+    /**
+     * Attempt to power cycle the chip.
+     * This is the implementation of hardReset, but extracted for clearer state signaling.
+     *
+     * @return true if the chip was restored to an operational state, false otherwise.
+     */
+    private boolean doRecovery() {
+        boolean succeeded = false;
+
+        // Can't do anything without the interface being up.
+        if (!setupHidl()) {
+            return false;
+        }
+
+        try {
+            int status;
+            status = mHidl.disable();
+            if (status != 0) {
+                Log.w(TAG, "Reset - Failed to disable: " + mHidl.translateStatus(status));
+            }
+            // Even if disable failed we can try to enable again anyways and hope it fixes the issue.
+
+            status = mHidl.enable();
+            if (status == 0) {
+                succeeded = true;
+            } else {
+                Log.e(TAG, "Reset - Failed to enable: " + mHidl.translateStatus(status));
+            }
+
+        } catch (RemoteException e) {
+            handleServerDeath();
+        }
+
+        return succeeded;
+    }
+
+    /**
+     * Attempt to bring the chip to an operational state. This is the implementation of enable,
+     * but extracted for clearer state signaling code.
+     * @return true if the chip and transport where successfully brought up, false otherwise.
+     */
+    private boolean doBringup() {
+        boolean succeeded = false;
+
+        // Make sure interface is up.
+        if (!setupHidl()) {
+            return false;
+        }
+
+        // Now perform actual enable operation.
+        try {
+            int status = mHidl.enable();
+            if (status == 0) {
+                succeeded = true;
+            } else {
+                Log.e(TAG, "Failed to enable ANT: " + mHidl.translateStatus(status));
+
+                // Try to clean things up.
+                status = mHidl.disable();
+                if (status != 0) {
+                    Log.w(TAG, "Disable failed: " + mHidl.translateStatus(status));
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Server died in enable.", e);
+            handleServerDeath();
+        }
+
+        return succeeded;
+    }
+
+    /**
+     * Update the internal state, and dispatch the update callback on a separate thread.
+     * @param newState The new internal state. A callback will only be dispatched if this is different than
+     *                 the previous state.
+     */
+    private void updateState(int newState) {
+
+        // Ignore non-updates.
+        if (newState == mState) {
+            return;
+        }
+
+        mState = newState;
+
+        // RESET is not actually a valid state, only used for signaling. In the case where it does occur we are
+        // in an enabled state.
+        if (mState == AntHalDefine.ANT_HAL_STATE_RESET) {
+            mState = AntHalDefine.ANT_HAL_STATE_ENABLED;
+        }
+
+        if (mUseKeepalive) {
+            // Change keep alive state based on whether we are now enabled or disabled.
+            int msg = (mState == AntHalDefine.ANT_HAL_STATE_ENABLED) ? KEEPALIVE_START : KEEPALIVE_STOP;
+            mKeepAliveHandler.sendMessage(mKeepAliveHandler.obtainMessage(msg));
+        }
+
+
+        // Dispatch state updates with a handler so that they don't block the current operation.
+        mStateDispatcher.post(new Runnable() {
+            @Override
+            public void run() {
+                // Cache since check+call is not atomic.
+                ICallback cachedCallbacks = mCallbacks;
+                if (cachedCallbacks != null) {
+                    cachedCallbacks.ANTStateChange(newState);
+                }
+            }
+        });
+    }
+
+    /**
+     * Listen for notifications about the server crashing.
+     */
+    private final DeathRecipient mDeathHandler = new DeathRecipient() {
+        @Override
+        public void serviceDied(long cookie) {
+            synchronized (mStateLock) {
+
+                // Only do anything if we don't already know about this death.
+                if (cookie != mDeathCookie) {
+                    return;
+                }
+
+                Log.e(TAG, "Detected asynchronous server death.");
+
+                handleServerDeath();
+                // Attempt automated recovery.
+                // Result is ignored, errors already logged to logcat and radio service notified by state callbacks.
+                hardReset();
+            }
+        }
+    };
+
+    /**
+     * Handle callbacks from the HIDL server.
+     */
+    private final IAntCallbacks mHidlCallbacks = new IAntCallbacks.Stub() {
+
+        @Override
+        public void onTransportDown(String cause) throws RemoteException {
+            synchronized (mStateLock) {
+                Log.e(TAG, "Transport is down: " + cause);
+                // Attempt automated recovery.
+                hardReset();
+            }
+        }
+
+        @Override
+        public void onMessageReceived(ArrayList<Byte> data) throws RemoteException {
+
+            // Translate to a byte array.
+            byte[] message = new byte[data.size()];
+            for (int i = 0; i < data.size(); i++) {
+                message[i] = data.get(i);
+            }
+
+            if (!keepaliveHandler(message) && !flowControlHandler(message)) {
+                // No one requested the message be filtered.
+                // Cache the callback since check+call is non atomic.
+                ICallback cachedCallbacks = mCallbacks;
+                if (cachedCallbacks != null) {
+                    cachedCallbacks.ANTRxMessage(message);
+                }
+            }
+        }
+
+        /**
+         * Message handling for keep alive monitoring.
+         *
+         * @return true if mesg was for keep alive purposes and should be filtered out.
+         */
+        private boolean keepaliveHandler(byte[] mesg) {
+            if (mUseKeepalive) {
+                // All activity resets the timers.
+                mKeepAliveHandler.sendMessage(mKeepAliveHandler.obtainMessage(KEEPALIVE_RESET));
+
+                return Arrays.equals(mesg, KEEPALIVE_RESP);
+            } else {
+                return false;
+            }
+        }
+
+        /**
+         * Message handling for data channel flow control.
+         * @return True if mesg was for flow control purposes and should be filtered out.
+         */
+        private boolean flowControlHandler(byte[] mesg) {
+            boolean filter = false;
+
+            if (mUseFlowControl) {
+                if (mesg[Mesg.ID_OFFSET] == Mesg.FLOW_CONTROL_ID) {
+
+                    filter = true;
+
+                    if (mesg[Mesg.DATA_OFFSET] == Mesg.FLOW_CONTROL_GO) {
+
+                        synchronized (mFlowControlLock) {
+                            mFlowGoReceived = true;
+                            mFlowControlLock.notify();
+                        }
+                    }
+                }
+            }
+
+            return filter;
+        }
+    };
+
+    private final Handler mKeepAliveHandler = new Handler() {
+
+        private boolean started = false;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case KEEPALIVE_START:
+                if (!started) {
+                    started = true;
+                    sendMessageDelayed(obtainMessage(KEEPALIVE_PING), KEEPALIVE_TIMEOUT_MS);
+                }
+                break;
+
+            case KEEPALIVE_STOP:
+                started = false;
+                removeMessages(KEEPALIVE_PING);
+                removeMessages(KEEPALIVE_TIMEOUT);
+                break;
+
+            case KEEPALIVE_RESET:
+                removeMessages(KEEPALIVE_PING);
+                removeMessages(KEEPALIVE_TIMEOUT);
+                if (started) {
+                    sendMessageDelayed(obtainMessage(KEEPALIVE_PING), KEEPALIVE_TIMEOUT_MS);
+                }
+                break;
+
+            case KEEPALIVE_PING:
+                // Safe to ignore the result, error will be detected either the next time the radio service sends a message
+                // or when the keepalive timeout expires.
+                ANTTxMessage(KEEPALIVE_MESG);
+                sendMessageDelayed(obtainMessage(KEEPALIVE_TIMEOUT), KEEPALIVE_TIMEOUT_MS);
+                break;
+
+            case KEEPALIVE_TIMEOUT:
+                Log.e(TAG, "No response to keepalive message, attempting recovery.");
+                // Return ignored for same reasons as in mDeathHandler.
+                hardReset();
+                break;
+            }
+        }
+    };
+}
diff --git a/src/com/dsi/ant/hidl/Mesg.java b/src/com/dsi/ant/hidl/Mesg.java
new file mode 100644
index 0000000..3b55040
--- /dev/null
+++ b/src/com/dsi/ant/hidl/Mesg.java
@@ -0,0 +1,22 @@
+package com.dsi.ant.hidl;
+
+// A subset of the message constants from ant lib that are needed
+// by the hal service.
+final class Mesg {
+    // Offsets within message structure.
+    static final int ID_OFFSET = 1;
+    static final int DATA_OFFSET = 2;
+
+    // Message ids.
+    static final byte BROADCAST_DATA_ID =           (byte) 0x4E;
+    static final byte ACKNOWLEDGED_DATA_ID =        (byte) 0x4F;
+    static final byte BURST_DATA_ID =               (byte) 0x50;
+    static final byte EXT_BROADCAST_DATA_ID =       (byte) 0x5D;
+    static final byte EXT_ACKNOWLEDGED_DATA_ID =    (byte) 0x5E;
+    static final byte EXT_BURST_DATA_ID =           (byte) 0x5F;
+    static final byte ADV_BURST_DATA_ID =           (byte) 0x72;
+    static final byte FLOW_CONTROL_ID =             (byte) 0xC9;
+
+    // Any other value means stop.
+    static final byte FLOW_CONTROL_GO =             (byte) 0x00;
+}
diff --git a/src/com/dsi/ant/server/AntService.java b/src/com/dsi/ant/server/AntService.java
index 67878c4..d1d3ce0 100644
--- a/src/com/dsi/ant/server/AntService.java
+++ b/src/com/dsi/ant/server/AntService.java
@@ -1,7 +1,7 @@
 /*
- * ANT Stack
+ * ANT Android Host Stack
  *
- * Copyright 2009 Dynastream Innovations
+ * Copyright 2009-2018 Dynastream Innovations
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@
 import android.content.IntentFilter;
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.Service;
 import android.os.Binder;
 import android.os.Build;
@@ -33,8 +33,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
-import com.dsi.ant.core.*;
-
+import com.dsi.ant.hidl.HidlClient;
 import com.dsi.ant.server.AntHalDefine;
 import com.dsi.ant.server.IAntHal;
 import com.dsi.ant.server.IAntHalCallback;
@@ -51,13 +50,6 @@
     private static final boolean HAS_MULTI_USER_API =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
 
-    /**
-     * This flag determines if background users are allowed to use the ANT radio or not. Note that
-     * even if this flag is set, the active foreground user always has priority for using the
-     * ANT radio.
-     */
-    private static final boolean ALLOW_BACKGROUND_USAGE = true;
-
     public static final String ANT_SERVICE = "AntService";
 
     /**
@@ -77,12 +69,17 @@
      */
     public static final String ACTION_REQUEST_DISABLE = "com.dsi.ant.server.action.REQUEST_DISABLE";
 
-    private JAntJava mJAnt = null;
+    private HidlClient mAnt = null;
 
     private boolean mInitialized = false;
 
     /**
-     * Flag for if Bluetooth needs to be enabled for ANT to enable
+     * Flag for whether usage by background users is allowed. Configured in res/values/config.xml
+     */
+    private boolean mAllowBackgroundUsage = false;
+
+    /**
+     * Flag for if Bluetooth needs to be enabled for ANT to enable. Configured in res/values/config.xml
      */
     private boolean mRequiresBluetoothOn = false;
 
@@ -144,7 +141,7 @@
                     }
                 }
             }
-            if(!ALLOW_BACKGROUND_USAGE)
+            if(!mAllowBackgroundUsage)
             {
                 if(HAS_MULTI_USER_API &&
                         Intent.ACTION_USER_SWITCHED.equals(action))
@@ -155,13 +152,6 @@
         }
     };
 
-    /**
-     * Checks if Bluetooth needs to be turned on for ANT to enable
-     */
-    private boolean requiresBluetoothOn() {
-        return false; // Set to true if require bluetooth on for ANT functionality
-    }
-
     public static boolean startService(Context context)
     {
         return ( null != context.startService(new Intent(IAntHal.class.getName())) );
@@ -246,7 +236,7 @@
                     // Check foreground user using ANT HAL Service permissions.
                     id = Binder.clearCallingIdentity();
                     UserHandle activeUser =
-                            ActivityManagerNative.getDefault().getCurrentUser().getUserHandle();
+                            ActivityManager.getService().getCurrentUser().getUserHandle();
                     isActiveUser = activeUser.equals(callingUser);
                 } catch (RemoteException e)
                 {
@@ -265,7 +255,7 @@
                     shouldSwitch = true;
                 }
 
-                if(ALLOW_BACKGROUND_USAGE)
+                if(mAllowBackgroundUsage)
                 {
                     // Allow anyone to become the current user if there is no current user.
                     if(mCurrentUser == null)
@@ -420,7 +410,7 @@
                 internalCall ||
                 Binder.getCallingUserHandle().equals(mCurrentUser))
         {
-            retState = mJAnt.getRadioEnabledStatus(); // ANT state is native state
+            retState = mAnt.getRadioEnabledStatus();
         }
 
         if(DEBUG) Log.i(TAG, "Get ANT State = "+ retState +" / "+ AntHalDefine.getAntHalStateString(retState));
@@ -490,9 +480,9 @@
         int ret = AntHalDefine.ANT_HAL_RESULT_FAIL_UNKNOWN;
         synchronized(sAntHalServiceDestroy_LOCK)
         {
-            if (mJAnt != null)
+            if (mAnt != null)
             {
-                if(JAntStatus.SUCCESS == mJAnt.enable())
+                if(mAnt.enable())
                 {
                     if(DEBUG) Log.v(TAG, "Enable call: Success");
                     ret = AntHalDefine.ANT_HAL_RESULT_SUCCESS;
@@ -516,17 +506,11 @@
         int ret = AntHalDefine.ANT_HAL_RESULT_FAIL_UNKNOWN;
         synchronized(sAntHalServiceDestroy_LOCK)
         {
-            if (mJAnt != null)
+            if (mAnt != null)
             {
-                if(JAntStatus.SUCCESS == mJAnt.disable())
-                {
-                    if(DEBUG) Log.v(TAG, "Disable callback end: Success");
-                    ret = AntHalDefine.ANT_HAL_RESULT_SUCCESS;
-                }
-                else
-                {
-                    if (DEBUG) Log.v(TAG, "Disable callback end: Failure");
-                }
+                mAnt.disable();
+                if(DEBUG) Log.v(TAG, "Disable callback end: Success");
+                ret = AntHalDefine.ANT_HAL_RESULT_SUCCESS;
             }
         }
         return ret;
@@ -582,30 +566,15 @@
 
         int result = AntHalDefine.ANT_HAL_RESULT_FAIL_UNKNOWN;
 
-        JAntStatus status = mJAnt.ANTTxMessage(message);
-
-        if(JAntStatus.SUCCESS == status)
+        if (mAnt.ANTTxMessage(message))
         {
-            if (DEBUG) Log.d (TAG, "mJAnt.ANTTxMessage returned status: " + status.toString());
+            if (DEBUG) Log.d (TAG, "mJAnt.ANTTxMessage returned success.");
 
             result = AntHalDefine.ANT_HAL_RESULT_SUCCESS;
         }
         else
         {
-            if (DEBUG) Log.w( TAG, "mJAnt.ANTTxMessage returned status: " + status.toString() );
-
-            if(JAntStatus.FAILED_BT_NOT_INITIALIZED == status)
-            {
-                result = AntHalDefine.ANT_HAL_RESULT_FAIL_NOT_ENABLED;
-            }
-            else if(JAntStatus.NOT_SUPPORTED == status)
-            {
-                result = AntHalDefine.ANT_HAL_RESULT_FAIL_NOT_SUPPORTED;
-            }
-            else if(JAntStatus.INVALID_PARM == status)
-            {
-                result = AntHalDefine.ANT_HAL_RESULT_FAIL_INVALID_REQUEST;
-            }
+            if (DEBUG) Log.w( TAG, "mJAnt.ANTTxMessage returned failure.");
         }
 
         if (DEBUG) Log.v(TAG, "ANTTxMessage: Result = "+ result);
@@ -696,9 +665,9 @@
         int ret = AntHalDefine.ANT_HAL_RESULT_FAIL_UNKNOWN;
         synchronized(sAntHalServiceDestroy_LOCK)
         {
-            if (mJAnt != null)
+            if (mAnt != null)
             {
-                if(JAntStatus.SUCCESS == mJAnt.hardReset())
+                if(mAnt.hardReset())
                 {
                     if(DEBUG) Log.v(TAG, "Hard Reset end: Success");
                     ret = AntHalDefine.ANT_HAL_RESULT_SUCCESS;
@@ -762,35 +731,24 @@
 
         super.onCreate();
 
-        if(null != mJAnt)
+        if(null != mAnt)
         {
             // This somehow happens when quickly starting/stopping an application.
             if (DEBUG) Log.e(TAG, "LAST JAnt HCI Interface object not destroyed");
         }
         // create a single new JAnt HCI Interface instance
-        mJAnt = new JAntJava();
-        mRequiresBluetoothOn = requiresBluetoothOn();
-        JAntStatus createResult = mJAnt.create(mJAntCallback);
-
-        if (createResult == JAntStatus.SUCCESS)
-        {
-            mInitialized = true;
-
-            if (DEBUG) Log.d(TAG, "JAntJava create success");
-        }
-        else
-        {
-            mInitialized = false;
-
-            if (DEBUG) Log.e(TAG, "JAntJava create failed: " + createResult);
-        }
+        mAnt = new HidlClient();
+        mAllowBackgroundUsage = getResources().getBoolean(R.bool.allow_background_usage);
+        mRequiresBluetoothOn = getResources().getBoolean(R.bool.requires_bluetooth_on);
+        mAnt.create(mAntCallback);
+        mInitialized = true;
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_REQUEST_ENABLE);
         filter.addAction(ACTION_REQUEST_DISABLE);
         if(HAS_MULTI_USER_API)
         {
-            if(!ALLOW_BACKGROUND_USAGE)
+            if(!mAllowBackgroundUsage)
             {
                 // If we don't allow background users, we need to monitor user switches to clear the
                 // active user.
@@ -815,13 +773,13 @@
         {
             synchronized(sAntHalServiceDestroy_LOCK)
             {
-                if(null != mJAnt)
+                if(null != mAnt)
                 {
                     int result = disableBlocking();
                     if (DEBUG) Log.d(TAG, "onDestroy: disable result is: " + AntHalDefine.getAntHalResultString(result));
 
-                    mJAnt.destroy();
-                    mJAnt = null;
+                    mAnt.destroy();
+                    mAnt = null;
                 }
             }
 
@@ -896,7 +854,7 @@
 
     // ----------------------------------------------------------------------------------------- JAnt Callbacks
 
-    private JAntJava.ICallback mJAntCallback = new JAntJava.ICallback()
+    private HidlClient.ICallback mAntCallback = new HidlClient.ICallback()
     {
         public synchronized void ANTRxMessage( byte[] message)
         {
diff --git a/vintf/README.txt b/vintf/README.txt
new file mode 100644
index 0000000..1c47b84
--- /dev/null
+++ b/vintf/README.txt
@@ -0,0 +1,7 @@
+The compatibility_matrix.xml file in this directory contains a fragment that must be merged into
+the framework compatibility matrix located at hardware/interfaces/compatibility_matrix.current.xml
+if the ANTHALService apk is included in the system image.
+
+The hal here is listed as non-optional, since the ANTHALService will consider being unable to
+bind an error.
+
diff --git a/vintf/compatibility_matrix.xml b/vintf/compatibility_matrix.xml
new file mode 100644
index 0000000..a062326
--- /dev/null
+++ b/vintf/compatibility_matrix.xml
@@ -0,0 +1,9 @@
+<hal format="hidl" optional="false">
+    <name>com.dsi.ant</name>
+    <version>1.0</version>
+    <interface>
+        <name>IAnt</name>
+        <instance>default</instance>
+    </interface>
+</hal>
+