I am using JSch to perform an SFTP download in Android. This is on a LAN with 802.11n and an SFTP server on a wired gigabit connection. I am getting about 8 mbytes/sec on a laptop (also 802.11n) with the same code but I am only getting 40kbytes/sec on Android. Are there some flags or something I need to turn on to get this to transfer faster? I have tried it on a Nexus 5 and a Nexus 6, both with 5.1. I tried a couple of apps and one downloaded the file at 230kbytes/sec and the other right around 40kbytes/sec so I'm guessing one of them has the same issue I do.
Here is my code:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread thread = new Thread() {
#Override
public void run() {
try {
JSch jsch = new JSch();
jsch.setConfig("StrictHostKeyChecking", "no");
Session session = jsch.getSession("ftptest", "192.168.1.205");
session.setPort(22);
session.setPassword("password");
session.connect();
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
SftpProgressMonitor monitor = new SftpProgressMonitor() {
long finalCount = 0;
long start = -1;
#Override
public void init(int op, String src, String dest, long max) {
start = System.currentTimeMillis();
}
#Override
public boolean count(long count) {
finalCount += count;
/* long took = (System.currentTimeMillis() - start) / 1000;
if (took > 0) {
Log.w("SFTP", "Transferred so far " + finalCount + " at speed bytes/sec " + (finalCount / took));
}*/
return true;
}
#Override
public void end() {
long took = (System.currentTimeMillis() - start) / 1000;
Log.w("SFTP", "Transferred " + finalCount + " in " + took + " speed bytes/sec " + (finalCount / took ));
}
};
InputStream stream = channel.get("file", monitor);
int read = -1;
byte[] bs = new byte[8192];
while((read = stream.read(bs)) >= 0){
//do nothing
}
} catch (JSchException e) {
e.printStackTrace();
} catch (SftpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
thread.start();
}
}
EDIT: It seems to be much faster (about 160kbytes/sec) if I tell it to write the stream into a file without giving me an InputStream. The code looks pretty different between the two get methods but even if I went that route, 160kbytes/sec is still much slower than I had hoped.
What version of Android are you testing with? Can you adb shell in and see what these settings are (when you're on wifi)?
cat /proc/sys/net/ipv4/tcp_rmem
cat /proc/sys/net/ipv4/tcp_wmem
And then if they are low (something like "4092 8760 11680"), then try setting them to larger values:
sudo echo "524288,1048576,2097152" > /proc/sys/net/ipv4/tcp_rmem
sudo echo "262144,524288,1048576" > /proc/sys/net/ipv4/tcp_wmem
Then try your test again.
This could be from a bug where the wifi buffers were being set too small in some cases (https://code.google.com/p/android/issues/detail?id=64706).
Have you tried wrapping your stream in a buffering stream?
BufferedInputStream bis = new BufferedInputStream(channel.get("file", monitor));
8192 bytes seems like a small buffer...
Also make sure you close your connections/streams, maybe you have a lot of outgoing connections you don't know about that's hogging the hardware?
Related
I have a Nexus 5X that is having intermittent receive problems. I have been able to run the code on an Asus tablet, two Samsung tablets, and a Kindle Fire with no issues. The code is based on this repository:
https://github.com/moonlight-stream/moonlight-common/blob/master/src/com/limelight/nvstream/av/video/VideoStream.java
Here is the pertinent code:
WifiManager wifiMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Player");
wifiLock.setReferenceCounted(false);
wifiLock.acquire();
rtp = new DatagramSocket(RTP_PORT);
rtp.setReceiveBufferSize(256*1024);
// Receive thread
Thread t = new Thread() {
#Override
public void run() {
while (!isInterrupted())
{
try {
// Pull the next buffer in the ring and reset it
buffer = ring[ringIndex].getBuffer();
// Read the video data off the network
packet.setData(buffer, 0, buffer.length);
rtp.receive(packet);
// Do processing here
} catch (IOException e) {
return;
}
}
}
};
threads.add(t);
t.setName("Video - Receive");
t.setPriority(Thread.MAX_PRIORITY - 1);
t.start();
I am as sure as I can be that the UDP stream is properly being sent to the device. Most of the packets are received correctly. However, I've noticed in a method trace that the
rtp.receive(packet);
can block for 100s-1000s of milliseconds. When running on a different device in the exact same architecture, I see essentially 100% packet success and a maximum block of maybe 60ms.
I have tried this on 2.4G, and 5G, on multiple routers. The problem follows the device.
The packets being sent are UDP directly to the device. There are no IP conflicts on the network.
Any thoughts on what might be going wrong? Thank you very much!
EDIT: I should note the CPU usage is around 2%, and the memory usage is around 10MB
EDIT 2: I created a bare application to simply investigate this issue. I see the same results. I'll see 30,000+ consecutive packets on several devices without a single drop. I'll drop a range of packets every couple seconds on a Nexus 5X. I've had someone remotely run this test application on their Nexus 5X, and they reported that no packets were dropped. All signs are pointing to a driver / radio issue as far as I know...
package com.udp_receiver_test;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity {
private WifiManager.WifiLock wifiLock;
Thread t;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Player");
wifiLock.setReferenceCounted(false);
wifiLock.acquire();
// Use sustained performance mode on N+ to ensure consistent
// CPU availability
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
getWindow().setSustainedPerformanceMode(true);
}
startListenThread();
}
private void startListenThread()
{
t = new Thread() {
#Override
public void run()
{
DatagramSocket socket;
int nextSequenceNumber = 0;
ByteBuffer byteBuffer;
int BufferSize = 1292;
int NumRingElements = 784;
long correctPacketCounter = 0;
byte ring[][] = new byte[NumRingElements][1292];
try {
socket = new DatagramSocket(12345);
socket.setReceiveBufferSize(256 * 1024);
} catch (Exception e) {
e.printStackTrace();
return;
}
byte[] buffer;
int ringCounter = 0;
DatagramPacket packet = new DatagramPacket(new byte[1], 1);
//short rtpSequenceNumber;
int sequenceNumber;
while (!isInterrupted()) {
try {
buffer = ring[ringCounter];
packet.setData(buffer, 0, buffer.length);
socket.receive(packet);
if (packet.getLength() > 20) {
// Read the RTP sequence number field (big endian)
byteBuffer = ByteBuffer.wrap(buffer);
byteBuffer.position(16);
sequenceNumber = (byteBuffer.getInt() >> 8) & 0xFFFFFF;
if (sequenceNumber != nextSequenceNumber) {
Log.d("TAG", "Not next sequence number - Got:" + sequenceNumber + " Expected:" + nextSequenceNumber);
} else {
if (((++correctPacketCounter) % 100) == 0)
{
Log.d("TAG", "Correct packets = " + correctPacketCounter);
}
}
nextSequenceNumber = (sequenceNumber + 256) % 65535;
} else {
Log.d("TAG", "Message too short");
}
ringCounter = (ringCounter + 1) % NumRingElements;
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
};
t.setName("Receiver");
t.setPriority(Thread.MAX_PRIORITY - 1);
t.start();
}
#Override
public void onStop()
{
super.onStop();
wifiLock.release();
if (t.isAlive()) {
t.interrupt();
}
}
}
I've been struggling with the problem of sending continuous data from arduino to Android.
What I want to do is get analog read convert it to 0-5V information, and send that information to Android app.
My arduino code is just simply:
//(...)defining pins and levels
SoftwareSerial BTSerial(rxPin, txPin);
void setup()
{
pinMode(getData, INPUT);
digitalWrite(keyPin, LOW);
BTSerial.begin(9600);
}
void loop()
{
contact = digitalRead(getData);
if (contact == HIGH) {
sensorValue = analogRead(sensorPin);
double voltage = sensorValue * (5.0 / 1023.0);
if (BTSerial.available()) {
Serial.write(BTSerial.read());
}
BTSerial.println(voltage, 3);
BTSerial.write("\r");
if (Serial.available()) {
BTSerial.write(Serial.read());
}
}
delay(5);
}
I need to send data informing about measurment with ~200Hz frequency.
After sending the data to application it seems that part of data is lost.
I tried higher bound rates but the problem still occurs. Is there a way to send continuous data from arduino using serial port without loosing some % of that data?
I think the problem is in the design of the receiver. I Solved BTL communication in .net Xamarin, but the principle should be the same. In Android reading from InputStream must be quick and can not use sleep. You need to use an endless cycle and there quick read data into temp buffer. Immediately a dune bytes to an auxiliary large buffer (use read / write cursor) and then, for example, in timer treat the data (I suppose you are using some packet protocol)
public override void Run()
{
WriteLogInfoToLog("ConnectedThread.Run() - before");
while (true)
{
try
{
int readBytes = 0;
lock (InternaldataReadLock)
{
readBytes = clientSocketInStream.Read(InternaldataRead, 0, InternaldataRead.Length);
Array.Copy(InternaldataRead, TempdataRead, readBytes);
}
if (readBytes > 0)
{
lock (dataReadLock)
{
dataRead = new byte[readBytes];
for (int i = 0; i < readBytes; i++)
{
dataRead[i] = TempdataRead[i];
}
}
}
}
catch (System.Exception e)
{
btlManager.btlState = BTLService.BTLState.Nothing;//Spadlo spojeni, musi spustit cele od zacatku
WriteLogInfoToLog("ConnectedThread.Run() - EXCEPTION " + e.Message + ", " + e.HResult + ", " + e.StackTrace + ", " + e.InnerException);
if (e is Java.IO.IOException)
{
}
else
{
}
break;
}
}
WriteLogInfoToLog("ConnectedThread.Run() - after");
}
I am running some performance tests and see some spikes in my tests results.
This is the code I use to measure the time of the writing to file.
public class Test extends AndroidTestCase {
private DecimalFormat decimalFormat = new DecimalFormat("#.##");
private final Boolean isInternal = true;
public void testWrite() {
Thread thread = new Thread() {
public void run() {
File dummy_file = createFile("dummy2", true);
File file = createFile("normal_write", isInternal);
for (int i = 0; i < 12500; i++) {
long start = System.nanoTime();
write(dummy_file, String.valueOf(i));
long stop = System.nanoTime();
double mSec = ((double) (stop - start) / 1000000.0);
write(file, decimalFormat.format(mSec));
}
}
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public File createFile(String fileName, Boolean isInternal) {
deleteFile(fileName, isInternal);
try {
String file_path = "";
if(isInternal == true) {
file_path = getContext().getFilesDir().getAbsolutePath() + "/" + fileName + ".dat";
} else {
file_path = Environment.getExternalStorageDirectory() + "/" + fileName + ".dat";
}
File file = new File(file_path);
file.createNewFile();
FileWriter fw = new FileWriter(file.getAbsoluteFile(), true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("##;##\n" +
"#LiveGraph demo file.\n" +
"Time");
bw.newLine();
bw.close();
return file;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void deleteFile(String fileName, Boolean isInternal) {
String file_path = "";
if ( isInternal == true) {
file_path = getContext().getFilesDir().getAbsolutePath() + "/" + fileName + ".dat";
} else {
file_path = Environment.getExternalStorageDirectory() + "/" + fileName + ".dat";
}
File file = new File(file_path);
file.delete();
}
public void write(File file, String content) {
try {
FileWriter fw = new FileWriter(file.getAbsoluteFile(), true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.newLine();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Mostly the time it takes to write is ~0.1-0.2ms but there are spikes which takes 10+ ms and can not figure out what the cause of this is. I can not link the graph here right now as I am a new user here.
I am really stuck with this any ideas?.
This is part of the time measurements.
0.16
0.16
0.17
0.15
0.15
2.5
0.17
0.16
0.16
0.16
0.15
0.17
0.17
0.17
0.19
0.19
0.17
0.2
0
0
4.79
0.24
0.23
0.28
0.23
0.28
0.03
0
11.23
0.16
0.15
0.18
0.16
0.16
0.17
0.16
5.84
It may be related to Android's storage access specifics:
Background on disks on phones Wait, what’s wrong with hitting the
disk? Android devices are all running flash memory, right? That’s like
a super-fast SSD with no moving parts? I shouldn’t have to care?
Unfortunately, you do.
You can’t depend on the flash components or filesystems used in most
Android devices to be consistently fast. The YAFFS filesystem used on
many Android devices, for instance, has a global lock around all its
operations. Only one disk operation can be in-flight across the entire
device. Even a simple “stat” operation can take quite a while if you
are unlucky. Other devices with more traditional block device-based
filesystems still occasionally suffer when the block rotation layer
decides to garbage collect and do some slow internal flash erase
operations. (For some good geeky background reading, see
lwn.net/Articles/353411)
The take-away is that the “disk” (or filesystem) on mobile devices is
usually fast, but the 90th percentile latencies are often quite poor.
Also, most filesystems slow down quite a bit as they get more full.
(See slides from Google I/O Zippy Android apps talk, linked off
code.google.com/p/zippy-android)
http://android-developers.blogspot.com/2010/12/new-gingerbread-api-strictmode.html
Moved from Android forums
Im at a loss here. It want to send some serial data from arduino to android but what I send is not what is received. For example, If I put Serial.write(5), on the android side I get 48. If I put Serial.write(6) I get 1. The same problem happens when I send characters. If I send a 't', on android I get a T with 2 points on top(as if its a character from another language). ??????? What's happening?
Arduino
int count = 5;
const unsigned int BAUD_RATE = 115200;
void setup() {
Serial.begin(BAUD_RATE);
delay(10000);
}
void loop() {
//Serial.print(count, DEC);
//Serial.print(count, HEX);
Serial.print(count, OCT);
//Serial.println(count, BIN);
//Serial.write(temp);
//Serial.write("t");
Serial.write(count);
count++;
delay(2000);
}
Android
RFComm: UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
byte[] buffer = new byte[255];
Handler myHandler1 = new Handler();
Runnable receiveValues = new Runnable() {
public void run() {
try {
while (connected) {
while (in.available() > 0) {
Log.v("Note",
"For value: " + String.valueOf(in.read(buffer)));
for (int x = 0; x < buffer.length; x++)
Log.v("Note", "A " + buffer[x]);
}
Thread.sleep(50);
}
} catch (Exception e) {
connected = false;
e.printStackTrace();
}
}
};
The timing is right, every two seconds something gets sent but still I get the wrong value. As you can also see, I tried other ways to write to the serial port on the arduino but none of them work. Another thing, the end of line(when I do Serial.println()) is consistently received as a 0 on android.
Running on android 2.3.7 where min sdk = 8 (android 2.2)
Answer is really simple... Forgot about int ranges(-32768 to +32767) or in this case unsigned int(0 - 65534) and when I directly entered the baud rate, it worked well.
Serial.begin(115200);
I am trying to transfer images quickly between my Android phone and my PC over wifi. I have written code to do this but it can 4-5 seconds to transfer one 640x480px image across. I am wondering is my method flawed and is there a faster way to do this?
Here is the Server code
void main(String[] args)
{
try {
ServerSocket serverSocket = new ServerSocket(5555);
Socket clientSocket = serverSocket.accept();
long startTime = System.currentTimeMillis();
InputStream clientInputStream = clientSocket.getInputStream();
BufferedImage BI = ImageIO.read(clientInputStream);
long endTime = System.currentTimeMillis();
ImageIO.write(BI,"png",new File("test.png"));
System.out.println((endTime - startTime) + " ms.");
} catch (IOException e)
{
e.printStackTrace();
}
}
}
Here is the Android client code
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Bitmap imageToSend = BitmapFactory.decodeResource(this.getResources(),R.drawable.img);
try
{
Socket socket = new Socket("192.168.1.1",5555);
imageToSend.compress(CompressFormat.PNG,0 , socket.getOutputStream());
}
catch (Exception e) {
e.printStackTrace();
}
}
Thank you for any help you can give.
Two things I can think of:
Use a buffered output stream in your output e.g.
new BufferedOutputStream(socket.getOutputStream());
Write the image to disk first. Then try to open sockets in parallel each transferring a different offset of the image (e.g. split the image to 3 jobs, transfer each in parallel to the others). This will workaround some TCP behaviors.