@Override
    public void run() {
        // InputStream.read(buf, offs, len)
        InputStream is;

        byte[] length_buffer = new byte[4];
        byte[] message_buffer = new byte[2048];
        int message_length;

        int num_touches;

        ByteBufferView view = new ByteBufferView(length_buffer);

        double timestamp;

        TouchPoint[] points;

        // XXX nicely hardcoded :)
        points = new TouchPoint[MAX_TOUCH_EVENTS];
        for (int i = 0; i < MAX_TOUCH_EVENTS; i++)
            points[i] = new TouchPoint();

        try {
            is = socket.getInputStream();

            // readFully(is, header_buffer, 6);

            while (true) {
                if (!readFully(is, length_buffer, INITIAL_MESSAGE_LENGTH))
                    return;

                view.initialize(length_buffer);
                message_length = view.getInt();
                // System.out.println("message_length: "+message_length);

                if (!readFully(is, message_buffer, message_length))
                    return;

                view.initialize(message_buffer);
                timestamp = view.getDouble();
                // System.out.println("timestamp "+timestamp);
                num_touches = view.getInt();
                // System.out.println("num touches "+num_touches);

                for (int i = 0; i < num_touches; i++) {
                    points[i].id = view.getInt();
                    points[i].state = view.getInt();
                    points[i].tx = view.getFloat();
                    points[i].ty = view.getFloat();
                }

                if (handler != null)
                    handler.OnTouchPoints(timestamp, points, num_touches);
            }

        } catch (Exception e) {
            System.out.println("Exception: " + e);
            return;
        }
    }