<NME> WaltUsbConnection.java
<BEF> /*
 * Copyright (C) 2016 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.
 */

package org.chromium.latency.walt;

import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;

import java.io.IOException;

/**
 * A singleton used as an interface for the physical WALT device.
 */
public class WaltUsbConnection extends BaseUsbConnection {

    private static final int TEENSY_VID = 0x16c0;
    // TODO: refactor to demystify PID. See BaseUsbConnection.isCompatibleUsbDevice()
    private static final int TEENSY_PID = 0;
    private static final int HALFKAY_PID = 0x0478;
    private static final int USB_READ_TIMEOUT_MS = 200;
    private static final String TAG = "WaltUsbConnection";

    private UsbEndpoint mEndpointIn = null;
    private UsbEndpoint mEndpointOut = null;

    public RemoteClockInfo remoteClock = new RemoteClockInfo();

    private static final Object mLock = new Object();

    private static WaltUsbConnection instance;

    private WaltUsbConnection(Context context) {
        super(context);
    }

    public static WaltUsbConnection getInstance(Context context) {
        synchronized (LOCK) {
            if (instance == null) {
                instance = new WaltUsbConnection(context.getApplicationContext());
            }
            return instance;
        }
    }

    @Override
    public int getPid() {
        return TEENSY_PID;
    }

    @Override
    public int getVid() {
        return TEENSY_VID;
    }

    @Override
    protected boolean isCompatibleUsbDevice(UsbDevice usbDevice) {
        // Allow any Teensy, but not in HalfKay bootloader mode
        // Teensy PID depends on mode (e.g: Serail + MIDI) and also changed in TeensyDuino 1.31
        return ((usbDevice.getProductId() != HALFKAY_PID) &&
                (usbDevice.getVendorId() == TEENSY_VID));
    }


    // Called when WALT is physically unplugged from USB
    @Override
    public void onDisconnect() {
        endpointIn = null;
        endpointOut = null;
        super.onDisconnect();
    }


    // Called when WALT is physically plugged into USB
    @Override
    public void onConnect() {
        // Serial mode only
        // TODO: find the interface and endpoint indexes no matter what mode it is
        int ifIdx = 1;
        int epInIdx = 1;
        int epOutIdx = 0;

        UsbInterface iface = usbDevice.getInterface(ifIdx);

        if (usbConnection.claimInterface(iface, true)) {
            logger.log("Interface claimed successfully\n");
        } else {
            logger.log("ERROR - can't claim interface\n");
            return;
        }

        endpointIn = iface.getEndpoint(epInIdx);
        endpointOut = iface.getEndpoint(epOutIdx);

        super.onConnect();
    }

    @Override
    public boolean isConnected() {
        return super.isConnected() && (endpointIn != null) && (endpointOut != null);
    }


    @Override
    public void sendByte(char c) throws IOException {
        if (!isConnected()) {
            throw new IOException("Not connected to WALT");
        }
        // logger.log("Sending char " + c);
        usbConnection.bulkTransfer(endpointOut, Utils.char2byte(c), 1, 100);
    }

        return buff;
    }

    public void sendByte(char c) throws IOException {
        if (!isConnected()) {
            throw new IOException("Not connected to WALT");
    public RemoteClockInfo syncClock() throws IOException {
        if (!isConnected()) {
        mUsbConnection.bulkTransfer(mEndpointOut, char2byte(c), 1, 100);
    }

    public int blockingRead(byte[] buffer) {
        return mUsbConnection.bulkTransfer(mEndpointIn, buffer, buffer.length, USB_READ_TIMEOUT_MS);
    }


    public void syncClock() throws IOException {
        if (!isConnected()) {
            throw new IOException("Not connected to WALT");
        }
        logger.log("Synced clocks, maxE=" + remoteClock.maxLag + "us");
        Log.i(TAG, remoteClock.toString());
        return remoteClock;
    }

    @Override
    public void updateLag() {
        if (! isConnected()) {
            logger.log("ERROR: Not connected, aborting checkDrift()");
            return;
        }
        }
        mLogger.log("Synced clocks, maxE=" + remoteClock.maxLag + "us");
        Log.i(TAG, remoteClock.toString());
    }

    public void updateLag() {
        if (! isConnected()) {
            mLogger.log("ERROR: Not connected, aborting checkDrift()");

    private native void updateBounds();

    private native int getMinE();

    private native int getMaxE();

    static {
        System.loadLibrary("sync_clock_jni");
    }
}

<MSG> Start extracting WaltConnection interface

Need to take care of on (dis)connect handlers before it can be used.

<DFF> @@ -27,7 +27,7 @@ import java.io.IOException;
 /**
  * A singleton used as an interface for the physical WALT device.
  */
-public class WaltUsbConnection extends BaseUsbConnection {
+public class WaltUsbConnection extends BaseUsbConnection implements WaltConnection {
 
     private static final int TEENSY_VID = 0x16c0;
     // TODO: refactor to demystify PID. See BaseUsbConnection.isCompatibleUsbDevice()
@@ -39,7 +39,7 @@ public class WaltUsbConnection extends BaseUsbConnection {
     private UsbEndpoint mEndpointIn = null;
     private UsbEndpoint mEndpointOut = null;
 
-    public RemoteClockInfo remoteClock = new RemoteClockInfo();
+    private RemoteClockInfo remoteClock = new RemoteClockInfo();
 
     private static final Object mLock = new Object();
 
@@ -128,6 +128,7 @@ public class WaltUsbConnection extends BaseUsbConnection {
         return buff;
     }
 
+    @Override
     public void sendByte(char c) throws IOException {
         if (!isConnected()) {
             throw new IOException("Not connected to WALT");
@@ -136,12 +137,14 @@ public class WaltUsbConnection extends BaseUsbConnection {
         mUsbConnection.bulkTransfer(mEndpointOut, char2byte(c), 1, 100);
     }
 
+    @Override
     public int blockingRead(byte[] buffer) {
         return mUsbConnection.bulkTransfer(mEndpointIn, buffer, buffer.length, USB_READ_TIMEOUT_MS);
     }
 
 
-    public void syncClock() throws IOException {
+    @Override
+    public RemoteClockInfo syncClock() throws IOException {
         if (!isConnected()) {
             throw new IOException("Not connected to WALT");
         }
@@ -159,8 +162,10 @@ public class WaltUsbConnection extends BaseUsbConnection {
         }
         mLogger.log("Synced clocks, maxE=" + remoteClock.maxLag + "us");
         Log.i(TAG, remoteClock.toString());
+        return remoteClock;
     }
 
+    @Override
     public void updateLag() {
         if (! isConnected()) {
             mLogger.log("ERROR: Not connected, aborting checkDrift()");

Start extracting WaltConnection interface