Why doesn't the MidiInputPort.flush() clear the buffer in Android? - android

I am working with android.media.midi and I am sending a bunch of midi data to a MidiInputPort with a delay value like this:
long start = System.nanoTime();
if (messages != null)
{
for (int i = 0; i < messages.length(); i++)
{
MidiNote note = MidiHelper.parseMessageForNote(messages.getString(i));
if (note != null)
{
byte[] buffer = new byte[32];
int numBytes = 0;
int channel = 1; // MIDI channels 1-16 are encoded as 0-15.
buffer[numBytes++] = (byte) (note.action + (channel - 1));
buffer[numBytes++] = (byte) note.note;
buffer[numBytes++] = (byte) note.velocity;
long delay = note.delay * 1000000;
midiInputPort.send(buffer, 0, numBytes, start + delay);
start = start + delay;
}
}
midiInputPort.flush();
}
You'll notice that I call flush immediately after sending all the data (just trying to get flush() to work) but it has no effect. The data still gets sent to the Midi port as if I never called flush. The documentation is pretty clear and simple for this function. It says "If you want to cancel events that you have scheduled in the future then call flush()." Is there something about this that I am missing? Any help is appreciated.

Is the output stream still open? Maybe the MIDI device you are sending the flush packet to doesn't support it?
Looking at the google source code, flush() calls MidiPortImpl.packFlush() which sends a single byte array of byte[1]=0x02 to the output stream as long as it is still open.
public void onFlush() throws IOException {
synchronized (mBuffer) {
if (mOutputStream == null) {
throw new IOException("MidiInputPort is closed");
}
int length = MidiPortImpl.packFlush(mBuffer);
mOutputStream.write(mBuffer, 0, length);
}
}
https://android.googlesource.com/platform/frameworks/base/+/master/media/java/android/media/midi/MidiInputPort.java
https://android.googlesource.com/platform/frameworks/base/+/master/media/java/android/media/midi/MidiPortImpl.java

Related

issue printing from app to thermal printer

Hi guys i'm creating a method to print an invoice from my app, but when i send the bytes to print it doesn't print all of the bytes i'm sending to the printer, the last bytes are getting cut all time resulting in an incomplete invoice, this is the code that i'm using at the moment:
public void Print(string nombreImpresora, string formatoFactura)
{
var listOfDevices = BluetoothAdapter.DefaultAdapter;
if (listOfDevices == null)
throw new Exception("No Bluetooth adapter found.");
if (!listOfDevices.IsEnabled)
throw new Exception("Bluetooth adapter is not enabled.");
var device = (from bd in listOfDevices.BondedDevices
where bd.Name == nombreImpresora
select bd).FirstOrDefault();
if (device == null)
throw new Exception("Named device not found.");
BluetoothSocket socket;
var uuid = device.GetUuids()?.ElementAt(0);
if (uuid != null)
{
socket = device.CreateInsecureRfcommSocketToServiceRecord(uuid.Uuid);
}
else
{
socket = device.CreateInsecureRfcommSocketToServiceRecord(UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));
}
if (socket.IsConnected)
{
return;
}
socket.Connect();
byte[] completeBuffer = Encoding.ASCII.GetBytes(formatoFactura);
Toast.MakeText(Forms.Context, Convert.ToString(completeBuffer.Length), ToastLength.Short).Show();
var bufferSize = 256;
int completedBufferLength = completeBuffer.Length;
List<byte[]> bufferList = new List<byte[]>();
for (int i = 0; i < completedBufferLength; i = i + bufferSize)
{
byte[] val = new byte[bufferSize];
if (completedBufferLength < i + bufferSize)
{
bufferSize = completedBufferLength - i;
}
Array.Copy(completeBuffer, i, val, 0, bufferSize);
bufferList.Add(val);
}
for (int j = 0; j < bufferList.Count; j++)
{
socket.OutputStream.Write(bufferList[j], 0, bufferList[j].Length);
}
socket.Close();
socket.Dispose();
}
i'm sending a string and converting it to bytes in the method above, the string is a custom invoice that i made with the invoice data from another page of my app.
The printer that i'm using is Bixolon SPP-R310, at this point i don't know if it is a printer related issue really.
¿Can anyone help me with this pls?
thanks in advance
First of all try to append some empty lines at the end of your string using "\n". If the output is still incomplete change device.CreateInsecureRfcommSocketToServiceRecord to device.createRfcommSocketToServiceRecord.

Android TCP connection receiving packets out of order

I have a Particle Photon microcontroller sending TCP packets over a hotspot WiFi network to an Android Phone. The microcontroller is acting as server, the phone as client.
The phone however is receiving some (but not all) of the packets out of order, despite the information being transmitted via tcp. It was my understanding that this would not happen - am i wrong, or is there something i can do to correct this?
Microcontroller Code:
// This #include statement was automatically added by the Particle IDE.
#include "databuffer5.h"
// This #include statement was automatically added by the Particle IDE.
#include "databuffer7.h"
const unsigned int localPort = 10000;
IPAddress remoteIP(a, b, c, d);
// An TCP instance to let us send and receive packets over wifi
TCPServer server = TCPServer(localPort);
TCPClient client;
// UDP Port used for two way communication
short msg_count = 0;
const int adcPin = A0;
int byteBuffer;
unsigned long loopTimer;
const int packetSize = 40; //number of bytes in packet - 10 ints with 4 bytes each
byte buffer[packetSize];
int j = 0;
int dataCount = 0; //dummy data that increments every loop point, to measure packet contiuity
//(creates a line with slope 1 as data)
void setup() {
// start the UDP
server.begin();
// Print your device IP Address via serial
Serial.begin(9600);
Serial.println(WiFi.localIP());
//Serial.println(System.ticksPerMicrosecond()); //returns 120, ie 120MHz
}
void loop()
{
if (client.connected())
{
loopTimer = millis(); //mark start time of loop
for (int i = 0; i < 10; i++)
{
//for testing connection
byteBuffer = i+j*10;
buffer[i*4] = ( (byteBuffer >> 24) & 0xFF); //take upper 8 bits
buffer[i*4+1] = ( (byteBuffer >> 16) & 0xFF); //take middle upper 8 bits
buffer[i*4+2] = ( (byteBuffer >> 8 ) & 0xFF); //take middle lower 8 bits
buffer[i*4+3] = ( byteBuffer & 0xFF); //take lower 8 bits
dataCount++;
if (i != 9)
{
while(millis() < (loopTimer+10*(i+1))); //ie do nothing for 10 ms (time is in ms, want to delay by exactly 10ms for each loop)
//goal here is to sample every 10ms, by delaying for the remaining time
//dont delay here for the last sample, as the udp packet will take time
//delay after instead
}
}
server.write(buffer, sizeof(buffer)); //using sizeof on a byte array so dont need to scale (ie scaling factor is 1)
j++;
if (j < 0){j = 0;}
while(millis() < (loopTimer+10*10)); //delay till 100ms after loops started
}
else
{
client = server.available();
}
}
Android (client) code:
async_udp = new AsyncTask<Void, int[], Void>() {
#Override
protected Void doInBackground(Void... params) {
byte b1[];
b1 = new byte[100];
while (serverActive) {
Socket socket = null; //previously this was DatagramSocket (UDP) - no Socket (TCP)
try {
//DatagramSocket s = new DatagramSocket(server_port, server_ip);
socket = new Socket(server_ip, server_port);
socket.setPerformancePreferences(1, 2, 2);
InputStream socketStream = socket.getInputStream();
DatagramPacket p1 = new DatagramPacket(b1, b1.length);
ByteBuffer wrapped;
int data[] = new int[10+1]; //first number for message data, second is for the message number
while (serverActive) //TODO include shutdown function
{
while (socketStream.available() < 39){}
socketStream.read(b1, 0, 40);
//packet structure is a char containing message number, and 10 shorts (2 bytes) containing data points (between 0 and 4096)
wrapped = ByteBuffer.wrap(Arrays.copyOfRange(b1, 0, 40)); // extract 40 bytes to convert to 10 ints
for (int i = 0; i < 10; i++) {
data[i] = wrapped.getInt();
}
String str = data.toString();
server_port = p1.getPort();
server_ip = p1.getAddress();
String str_msg = "RECEIVED FROM CLIENT IP =" + server_ip + " port=" + server_port + " message no = " + b1[0] +
" data=" + str; //first character is message number
statusText = str_msg;
publishProgress(data);
}
socketStream.close();
socket.close();
} catch (SocketException e) {
if (socket != null) {}
//status.append("Error creating socket");
statusText = (" Error creating socket"); //this doesn't work!
} catch (IOException e) {
//status.append("Error recieving packet");
statusText = (" Error receiving packet"); //this doesn't work!
}
try{
Thread.sleep(100, 0); //sleep for 10ms if no wifi lock is found, to stop battery being silly
} catch(Exception e){
e.printStackTrace();
}
}
return null;
}
protected void onProgressUpdate(int[]... data1)
{
super.onProgressUpdate(data1);
int data[] = data1[0];
//send data to graph
for (int i = 0; i < 9; i++)
{
series.appendData(new DataPoint(lastDataX++, data[i]), false, graphPointsMax);
//append 9 points to graph, but only redraw the grpah on the 10th
}
series.appendData(new DataPoint(lastDataX++, data[9]), true, graphPointsMax);
}
};
if (Build.VERSION.SDK_INT >= 11)
{
async_udp.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else
{
async_udp.execute();
}
What is guaranteed by TCP, is that when you send a message - it will come in the right order, even if split by the TCP stack along the way (see TCP Segment Number, which serves as an indication which split message part belongs together with which, and in what order). So, in your case, all you are sending using:
server.write(buffer, sizeof(buffer));
no meter how big is the buffer (within the limits of TCP protocol, of course) is guaranteed to arrive in the right order and complete.
Sending several buffers one-by-one over TCP will provide you guaranteed delivery, or error notification (if any), but not the order of the messages sent. Down to the protocol level, these packets will contain different Sequence Numbers, and therefore will be treated by the TCP stack of the receiver separately, in the order the stack prefers.
I see two relatively simple things that could be done here:
try to put all you can in one buffer (not really practical approach, though)
Introduce the counter and increment each time you are sending the buffer out. Put the counter along with the buffer and check the precedence on the receiving side (phone, in your case).

Packet Sent but cannot Received Packets

I've been editing androids toyvpn sample project for vpn and i got this one for my sample app
I know there is something wrong/missing with my code because when i manually set up the vpn via android settings, there are packets Receive that's why
i've been searching how to receive packets and i dont know how to get this working.
here is my source code that VCL that extends VpnService
import android.app.PendingIntent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
/**
* Created by Jameshwart Lopez on 8/18/15.
*/
public class VCL extends VpnService {
private static final String TAG = "VpnClientLibrary";
private Thread mThread;
private ParcelFileDescriptor mInterface;
private String mServerAddress;
private String mServerPort;
private PendingIntent mConfigureIntent;
private String mParameters;
//a. Configure a builder for the interface.
Builder builder = new Builder();
public void vclRun(){
try {
//a. Configure the TUN and get the interface.
mInterface = builder.setSession("thesessionname")
.addAddress("192.168.0.1",24)
.addDnsServer("8.8.8.8")
.addRoute("0.0.0.0", 0).establish();
//b. Packets to be sent are queued in this input stream.
FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());
//b. Packets received need to be written to this output stream.
FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor());
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);
//c. The UDP channel can be used to pass/get ip package to/from server
DatagramChannel tunnel = DatagramChannel.open();
// Connect to the server, localhost is used for demonstration only.
mServerAddress="";//some of the vpn ip address here
mServerPort="1723";
InetSocketAddress server = new InetSocketAddress(mServerAddress, Integer.parseInt(mServerPort) );
tunnel.connect(server);
// For simplicity, we use the same thread for both reading and
// writing. Here we put the tunnel into non-blocking mode.
tunnel.configureBlocking(false);
// Authenticate and configure the virtual network interface.
handshake(tunnel);
//d. Protect this socket, so package send by it will not be feedback to the vpn service.
protect(tunnel.socket());
int timer = 0;
//e. Use a loop to pass packets.
while (true) {
//get packet with in
//put packet to tunnel
//get packet form tunnel
//return packet with out
//sleep is a must
// Assume that we did not make any progress in this iteration.
boolean idle = true;
// Read the outgoing packet from the input stream.
int length = in.read(packet.array());
if (length > 0) {
// Write the outgoing packet to the tunnel.
packet.limit(length);
tunnel.write(packet);
packet.clear();
// There might be more outgoing packets.
idle = false;
// If we were receiving, switch to sending.
if (timer < 1) {
timer = 1;
}
}
// Read the incoming packet from the tunnel.
length = tunnel.read(packet);
if (length > 0) {
// Ignore control messages, which start with zero.
if (packet.get(0) != 0) {
// Write the incoming packet to the output stream.
out.write(packet.array(), 0, length);
}
packet.clear();
// There might be more incoming packets.
idle = false;
// If we were sending, switch to receiving.
if (timer > 0) {
timer = 0;
}
}
// If we are idle or waiting for the network, sleep for a
// fraction of time to avoid busy looping.
if (idle) {
Thread.sleep(100);
// Increase the timer. This is inaccurate but good enough,
// since everything is operated in non-blocking mode.
timer += (timer > 0) ? 100 : -100;
// We are receiving for a long time but not sending.
if (timer < -15000) {
// Send empty control messages.
packet.put((byte) 0).limit(1);
for (int i = 0; i < 3; ++i) {
packet.position(0);
tunnel.write(packet);
}
packet.clear();
// Switch to sending.
timer = 1;
}
// We are sending for a long time but not receiving.
//if (timer > 20000) {
// throw new IllegalStateException("Timed out");
//}
}
}
} catch (Exception e) {
// Catch any exception
e.printStackTrace();
} finally {
try {
if (mInterface != null) {
mInterface.close();
mInterface = null;
}
} catch (Exception e) {
}
}
}
private void handshake(DatagramChannel tunnel) throws Exception {
// To build a secured tunnel, we should perform mutual authentication
// and exchange session keys for encryption. To keep things simple in
// this demo, we just send the shared secret in plaintext and wait
// for the server to send the parameters.
// Allocate the buffer for handshaking.
ByteBuffer packet = ByteBuffer.allocate(1024);
// Control messages always start with zero.
String password = "";//vpn password here
packet.put((byte) 0).put(password.getBytes()).flip();
// Send the secret several times in case of packet loss.
for (int i = 0; i < 3; ++i) {
Log.e("packetsdata", packet.toString());
packet.position(0);
tunnel.write(packet);
}
packet.clear();
// Wait for the parameters within a limited time.
for (int i = 0; i < 50; ++i) {
Thread.sleep(100);
// Normally we should not receive random packets.
int length = tunnel.read(packet);
if (length > 0 && packet.get(0) == 0) {
configure(new String(packet.array(), 1, length - 1).trim());
return;
}
}
//throw new IllegalStateException("Timed out");
}
private void configure(String parameters) throws Exception {
// If the old interface has exactly the same parameters, use it!
if (mInterface != null) {
Log.i(TAG, "Using the previous interface");
return;
}
// Configure a builder while parsing the parameters.
Builder builder = new Builder();
for (String parameter : parameters.split(" ")) {
String[] fields = parameter.split(",");
try {
switch (fields[0].charAt(0)) {
case 'm':
builder.setMtu(Short.parseShort(fields[1]));
break;
case 'a':
builder.addAddress(fields[1], Integer.parseInt(fields[2]));
break;
case 'r':
builder.addRoute(fields[1], Integer.parseInt(fields[2]));
break;
case 'd':
builder.addDnsServer(fields[1]);
break;
case 's':
builder.addSearchDomain(fields[1]);
break;
}
} catch (Exception e) {
throw new IllegalArgumentException("Bad parameter: " + parameter);
}
}
// Close the old interface since the parameters have been changed.
try {
mInterface.close();
} catch (Exception e) {
// ignore
}
// Create a new interface using the builder and save the parameters.
mInterface = builder.setSession(mServerAddress)
.setConfigureIntent(mConfigureIntent)
.establish();
mParameters = parameters;
Log.i(TAG, "New interface: " + parameters);
}
}
this is how i use the class above
private Thread mThread;
/*
* Services interface
* */
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Start a new session by creating a new thread.
mThread = new Thread(this, "VpnRunnable");
//start the service
mThread.start();
/*
*service is left "started" and will later be restarted by the system
* http://android-developers.blogspot.com.au/2010/02/service-api-changes-starting-with.html
*/
return START_STICKY;
}
#Override
public void onDestroy() {
if (mThread != null) {
mThread.interrupt();
}
super.onDestroy();
}
#Override
public synchronized void run() {
/*
* to run the vpn interface call the vclRun method inside VCL class
* */
this.vclRun();
}
Firstly, check that there are bytes being sent to your android device. As it won't be reading anything if there is nothing to receive.
Then have a look at this, as it may be messing up your connection.
You need to include this in the onStartCommand:
// The handler is only used to show messages.
if (mHandler == null) {
mHandler = new Handler(this);
}
// Stop the previous session by interrupting the thread.
if (mThread != null) {
mThread.interrupt();
}
// Extract information from the intent.
String prefix = getPackageName();
mServerAddress = intent.getStringExtra(prefix + ".ADDRESS");
mServerPort = intent.getStringExtra(prefix + ".PORT");
mSharedSecret = intent.getStringExtra(prefix + ".SECRET").getBytes();
// Start a new session by creating a new thread.
mThread = new Thread(this, "ToyVpnThread");
mThread.start();
return START_STICKY;
And also the details (some shown below) of the sychronized void.
#Override
public synchronized void run() {
try {
Log.i(TAG, "Starting");
// If anything needs to be obtained using the network, get it now.
// This greatly reduces the complexity of seamless handover, which
// tries to recreate the tunnel without shutting down everything.
// In this demo, all we need to know is the server address.
InetSocketAddress server = new InetSocketAddress(
mServerAddress, Integer.parseInt(mServerPort));
// We try to create the tunnel for several times. The better way
// is to work with ConnectivityManager, such as trying only when
// the network is avaiable. Here we just use a counter to keep
// things simple.
for (int attempt = 0; attempt < 10; ++attempt) {
mHandler.sendEmptyMessage(R.string.connecting);
// Reset the counter if we were connected.
// See BELOW
if (run(server)) {
attempt = 0;
}
// Sleep for a while. This also checks if we got interrupted.
Thread.sleep(3000);
} /..../
You are not managing your thread actions well. It is advised to receive any bytes that need to be received before attempting your run. That not doing so can cause problems.
I would go back through your code and put in the things you took out.
I also suggest you change your code here:
packet.put((byte) 0).put(password.getBytes()).flip();
Try to use explicit encoding:
packet.put((byte) 0).put(password.getBytes("UTF-8")).flip();
As data can be lost without it. See this answer:
https://stackoverflow.com/a/7947911/3956566
I have checked and your project is using "UTF-8".
Let me know if this doesn't help.

Get String from bluetooth buffer

I'm developing an app to communicate with a PCB board via bluetooth.
I receive a string from the PCB board to my app every 50ms. This string has the next structure:
start_byte(1byte)/battery _level(1byte)/speed(1byte)/mode(1byte)
So I'll receive a string like this (I'll put it in hex):
80464B11
each 50ms.
This is the code. First this is the ConnectedThread which listens for the communication and that sends the received message to the mainActivity:
public void run() {
byte[] buffer = new byte[1024];
int readed;
while (true) {
try {
readed = inputStream.read(buffer);
if (readed > 0) {
final byte[] temp = new byte [readed];
System.arraycopy(buffer, 0, temp, 0, readed);
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
/*Sends message to UI*/
connectionListener.msgRead(temp);
}
});
}
} catch (IOException e) {
...
break;
}
}
Then in MainActivity I operate with the received string to extract from it each value.
#Override
public void msgRead(byte[] buffer) {
String income = byteArrayToHex(buffer);
...
Here the next step would be to check for the start_byte and after this, get the other values.
But here comes my doubt. This string will be received each 50ms, so I'll be receiving something like this:
80464B1180464B1180464B1180464B1180464B1180464B1180464B1180464B1180464B11...
So, what I do to check for the start_byte is this:
String start_byte = income.substring(0, 2);
And then, if that matches with the start_byte value, I extract the rest of the values:
if (start_byte.equals("80")) {
...
Is my approach correct to face this? Won't the buffer overflow? How can I correctly check for the start_byte to the get the other values?
maybe it is usefull to just use the read() function. This function is blocking until one byte has been read. So you can make something like this:
int[] yourArray = new int[4];
for(int i = 0; i < 4; i++)
{
yourArray[i] = inputStream.read();
}
so now your string is devived in 4 int's stored in a array.
maybe this helps you out in some sort of way
I have faced the problem this way. I've created a Queue in the ConnectedThread. Each time I receive a byte[] I put it into the Queue.
LinkedList<Byte> dataQueue = new LinkedList<Byte>();
int i = 0;
while (i< temp.length) {
dataQueue.add(temp[i]);
i++;
}
Then, when I want to get them I do:
byte readed_byte = dataQueue.pop();
This way I get a byte from the head of the queue each time I do pop().

How to read and write data through Java Nio client in Android

I am doing Client server communication in java successfully but now i need to write client in Android rather the java.
client: public class ExampleClient2 {
public static void main(String[] args) throws IOException,
InterruptedException {
int port = 1114;
SocketChannel channel = SocketChannel.open();
// we open this channel in non blocking mode
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("192.168.1.88", port));
if(!channel.isConnected())
{
while (!channel.finishConnect()) {
System.out.println("still connecting");
}
}
System.out.println("connected...");
while (true) {
// see if any message has been received
ByteBuffer bufferA = ByteBuffer.allocate(60);
int count = 0;
String message = "";
while ((count = channel.read(bufferA)) > 0) {
// flip the buffer to start reading
bufferA.flip();
message += Charset.defaultCharset().decode(bufferA);
}
if (message.length() > 0) {
System.out.println("message " + message);
if(message.contains("stop"))
{
System.out.println("Has stop messages");
// break;
}
else
{
// write some data into the channel
CharBuffer buffer = CharBuffer.wrap("Hello Server stop from client2 from 88");
while (buffer.hasRemaining()) {
channel.write(Charset.defaultCharset().encode(buffer));
}
}
message = "";
}
}
}
}
this code is running successfully in java but in android it consuming lots of memory and not running reliably, due to its while (true) loop its like polling , plz let me know some solution that without polling i can read and write the data.
Thanks.
You need to compact() the buffer after calling decode() (or get(), or write(), anything that takes data out of the buffer).
Youu shouldn't allocate a new buffer every time around that while loop, and you should break out of it if read() returned -1. I don't actually see a need for the while loop at all.

Categories

Resources