Android ServerSocket programming with jCIFS streaming files - android

I've got a bit of an issue and I've been asking regarding it quite a few times, but I think I'm one step closer now, so hopefully someone can help me with the rest.
My previous questions:
Connect to NAS device from Android
How to open files in Android with default viewer using jCIFS
Put simply - I want to create an application that:
Can connect to a NAS device using jCIFS
Is capable of launching files in the default viewer - i.e. a video in the video player
The first part is relatively easy and I've already done that, but the second part is what's troubling me and what I've asked about a few times before. I think I've made some progress though.
I think I need to use a ServerSocket in my application to somehow create a bridge between the NAS and the application that's playing the content. I'm thinking this could be done using a Service. The files from the NAS device can be accessed as a FileInputStream.
There are plenty of applications on Market (i.e. ES File Explorer) that are capable of doing this without root access, so I know it's possible - at the moment I just don't know how.
I've been looking at Logcat while using some of the aforementioned applications, and they all seem to be creating a local server and then launch a video Intent from that server. How can this be achieved?

Basic answer is to use SmbFileInputStream to get InputStream You probably use this.
Now the tricky part is how to offer InputStream to other apps.
One possible approach, how many apps provide streaming of any InputStream to other apps on device, is to use http: URL scheme, and tunel your stream over http.
Then apps that can handle http URLs can open and use your data.
For this you have to make some kind of http server, which sounds difficult, but actually is achievable task. Good source to start with is nanohttpd library which is just one java source, originally used to list files in dirs, but you can adapt it to stream your InputStream over http. That's what I did with success.
Your url would look like http:// localhost:12345 where 12345 is port on which your server listens for requests. This port may be obtained from ServerSocket.getLocalPort(). Then give this URL to some app and your server waits for connection and sends data.
A note about http streaming: some apps (e.g. video players) like seekable http streams (http Range header). Since you can get also SmbRandomAccessFile, you can make your tiny server to provide any part of data in file. Android's built-in video player needs such seekable http stream in order to allow seeking in video file, otherwise it gives "Video can't be played" error. Your server must be ready to handle disconnects and multiple connects with different Range values.
Basic tasks of http server:
create ServerSocket
create Thread waiting for connection (Socket accept = serverSocket.accept()), one thread may be ok since you'd handle single client at a time
read http request (socket.getInputStream()), mainly check GET method and Range header)
send headers, mainly Content-Type, Content-Length, Accept-Ranges, Content-Range headers
send actual binary data, which is plain copying of InputStream (file) to OutputStream (socket)
handle disconnects, errors, exceptions
Good luck in implementation.
EDIT:
Here's my class that does the thing. It references some non-present classes for file, which should be trivial for you to replace by your file class.
/**
* This is simple HTTP local server for streaming InputStream to apps which are capable to read data from url.
* Random access input stream is optionally supported, depending if file can be opened in this mode.
*/
public class StreamOverHttp{
private static final boolean debug = false;
private final Browser.FileEntry file;
private final String fileMimeType;
private final ServerSocket serverSocket;
private Thread mainThread;
/**
* Some HTTP response status codes
*/
private static final String
HTTP_BADREQUEST = "400 Bad Request",
HTTP_416 = "416 Range not satisfiable",
HTTP_INTERNALERROR = "500 Internal Server Error";
public StreamOverHttp(Browser.FileEntry f, String forceMimeType) throws IOException{
file = f;
fileMimeType = forceMimeType!=null ? forceMimeType : file.mimeType;
serverSocket = new ServerSocket(0);
mainThread = new Thread(new Runnable(){
#Override
public void run(){
try{
while(true) {
Socket accept = serverSocket.accept();
new HttpSession(accept);
}
}catch(IOException e){
e.printStackTrace();
}
}
});
mainThread.setName("Stream over HTTP");
mainThread.setDaemon(true);
mainThread.start();
}
private class HttpSession implements Runnable{
private boolean canSeek;
private InputStream is;
private final Socket socket;
HttpSession(Socket s){
socket = s;
BrowserUtils.LOGRUN("Stream over localhost: serving request on "+s.getInetAddress());
Thread t = new Thread(this, "Http response");
t.setDaemon(true);
t.start();
}
#Override
public void run(){
try{
openInputStream();
handleResponse(socket);
}catch(IOException e){
e.printStackTrace();
}finally {
if(is!=null) {
try{
is.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
private void openInputStream() throws IOException{
// openRandomAccessInputStream must return RandomAccessInputStream if file is ssekable, null otherwise
is = openRandomAccessInputStream(file);
if(is!=null)
canSeek = true;
else
is = openInputStream(file, 0);
}
private void handleResponse(Socket socket){
try{
InputStream inS = socket.getInputStream();
if(inS == null)
return;
byte[] buf = new byte[8192];
int rlen = inS.read(buf, 0, buf.length);
if(rlen <= 0)
return;
// Create a BufferedReader for parsing the header.
ByteArrayInputStream hbis = new ByteArrayInputStream(buf, 0, rlen);
BufferedReader hin = new BufferedReader(new InputStreamReader(hbis));
Properties pre = new Properties();
// Decode the header into params and header java properties
if(!decodeHeader(socket, hin, pre))
return;
String range = pre.getProperty("range");
Properties headers = new Properties();
if(file.fileSize!=-1)
headers.put("Content-Length", String.valueOf(file.fileSize));
headers.put("Accept-Ranges", canSeek ? "bytes" : "none");
int sendCount;
String status;
if(range==null || !canSeek) {
status = "200 OK";
sendCount = (int)file.fileSize;
}else {
if(!range.startsWith("bytes=")){
sendError(socket, HTTP_416, null);
return;
}
if(debug)
BrowserUtils.LOGRUN(range);
range = range.substring(6);
long startFrom = 0, endAt = -1;
int minus = range.indexOf('-');
if(minus > 0){
try{
String startR = range.substring(0, minus);
startFrom = Long.parseLong(startR);
String endR = range.substring(minus + 1);
endAt = Long.parseLong(endR);
}catch(NumberFormatException nfe){
}
}
if(startFrom >= file.fileSize){
sendError(socket, HTTP_416, null);
inS.close();
return;
}
if(endAt < 0)
endAt = file.fileSize - 1;
sendCount = (int)(endAt - startFrom + 1);
if(sendCount < 0)
sendCount = 0;
status = "206 Partial Content";
((RandomAccessInputStream)is).seek(startFrom);
headers.put("Content-Length", "" + sendCount);
String rangeSpec = "bytes " + startFrom + "-" + endAt + "/" + file.fileSize;
headers.put("Content-Range", rangeSpec);
}
sendResponse(socket, status, fileMimeType, headers, is, sendCount, buf, null);
inS.close();
if(debug)
BrowserUtils.LOGRUN("Http stream finished");
}catch(IOException ioe){
if(debug)
ioe.printStackTrace();
try{
sendError(socket, HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
}catch(Throwable t){
}
}catch(InterruptedException ie){
// thrown by sendError, ignore and exit the thread
if(debug)
ie.printStackTrace();
}
}
private boolean decodeHeader(Socket socket, BufferedReader in, Properties pre) throws InterruptedException{
try{
// Read the request line
String inLine = in.readLine();
if(inLine == null)
return false;
StringTokenizer st = new StringTokenizer(inLine);
if(!st.hasMoreTokens())
sendError(socket, HTTP_BADREQUEST, "Syntax error");
String method = st.nextToken();
if(!method.equals("GET"))
return false;
if(!st.hasMoreTokens())
sendError(socket, HTTP_BADREQUEST, "Missing URI");
while(true) {
String line = in.readLine();
if(line==null)
break;
// if(debug && line.length()>0) BrowserUtils.LOGRUN(line);
int p = line.indexOf(':');
if(p<0)
continue;
final String atr = line.substring(0, p).trim().toLowerCase();
final String val = line.substring(p + 1).trim();
pre.put(atr, val);
}
}catch(IOException ioe){
sendError(socket, HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
}
return true;
}
}
/**
* #param fileName is display name appended to Uri, not really used (may be null), but client may display it as file name.
* #return Uri where this stream listens and servers.
*/
public Uri getUri(String fileName){
int port = serverSocket.getLocalPort();
String url = "http://localhost:"+port;
if(fileName!=null)
url += '/'+URLEncoder.encode(fileName);
return Uri.parse(url);
}
public void close(){
BrowserUtils.LOGRUN("Closing stream over http");
try{
serverSocket.close();
mainThread.join();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Returns an error message as a HTTP response and
* throws InterruptedException to stop further request processing.
*/
private static void sendError(Socket socket, String status, String msg) throws InterruptedException{
sendResponse(socket, status, "text/plain", null, null, 0, null, msg);
throw new InterruptedException();
}
private static void copyStream(InputStream in, OutputStream out, byte[] tmpBuf, long maxSize) throws IOException{
while(maxSize>0){
int count = (int)Math.min(maxSize, tmpBuf.length);
count = in.read(tmpBuf, 0, count);
if(count<0)
break;
out.write(tmpBuf, 0, count);
maxSize -= count;
}
}
/**
* Sends given response to the socket, and closes the socket.
*/
private static void sendResponse(Socket socket, String status, String mimeType, Properties header, InputStream isInput, int sendCount, byte[] buf, String errMsg){
try{
OutputStream out = socket.getOutputStream();
PrintWriter pw = new PrintWriter(out);
{
String retLine = "HTTP/1.0 " + status + " \r\n";
pw.print(retLine);
}
if(mimeType!=null) {
String mT = "Content-Type: " + mimeType + "\r\n";
pw.print(mT);
}
if(header != null){
Enumeration<?> e = header.keys();
while(e.hasMoreElements()){
String key = (String)e.nextElement();
String value = header.getProperty(key);
String l = key + ": " + value + "\r\n";
// if(debug) BrowserUtils.LOGRUN(l);
pw.print(l);
}
}
pw.print("\r\n");
pw.flush();
if(isInput!=null)
copyStream(isInput, out, buf, sendCount);
else if(errMsg!=null) {
pw.print(errMsg);
pw.flush();
}
out.flush();
out.close();
}catch(IOException e){
if(debug)
BrowserUtils.LOGRUN(e.getMessage());
}finally {
try{
socket.close();
}catch(Throwable t){
}
}
}
}
/**
* Seekable InputStream.
* Abstract, you must add implementation for your purpose.
*/
abstract class RandomAccessInputStream extends InputStream{
/**
* #return total length of stream (file)
*/
abstract long length();
/**
* Seek within stream for next read-ing.
*/
abstract void seek(long offset) throws IOException;
#Override
public int read() throws IOException{
byte[] b = new byte[1];
read(b);
return b[0]&0xff;
}
}

In Samsung S5 (Android version 5.1.1), I faced a problem of range request starting from a value greater than the file size and I solved it by setting status = "200 OK" as below:
if (startFrom >= contentLength) {
// when you receive a request from MediaPlayer that does not contain Range in the HTTP header , then it is requesting a new stream
// https://code.google.com/p/android/issues/detail?id=3031
status = "200 OK";
}
The remaining headers were left as a fresh request for the stream

Related

Sending a photo from a device using Python3 and receiving it on an Android Device via TCP gives an OOM on the Android Device

I am trying to send a photo from my Raspberry Pi using Python 3, to my Android Device. I am doing this over TCP with the Pi as the Client, and the Android Device as my Server. My goal is to send a photo file from Pi to my Android Device. My Android Device would then decode that photo data and then set it as the drawable for an ImageView in my App. Kindly note that I'm sending a 200kB image that's 640x480.
I have tried a set up where the Pi sends text to my Android Device via TCP, and I've had success with that.
What I did next was to attempt to send a photo from a Python3 client to a Python3 Server. In this case, I used the Pi still as my client, and I used my MacOS Laptop as the Server. This are the code that I ended up using.
Server - MAC OS
import socket # Import socket module
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
port = 11111 # Reserve a port for your service.
s.bind((host, port)) # Bind to the port
f = open('torecv.jpg','wb')
s.listen(5) # Now wait for client connection.
while True:
c, addr = s.accept() # Establish connection with client.
print('Got connection from', addr)
print("Receiving...")
l = c.recv(1024)
while (l):
print("Receiving...")
f.write(l)
l = c.recv(1024)
f.close()
print("Done Receiving")
c.send(b'Thank you for connecting')
c.shutdown(socket.SHUT_RDWR)
c.close() # Close the connection
Client - Pi
import socket # Import socket module
import os
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
host = '192.168.254.194' # Get local machine name
port = 6001 # Reserve a port for your service.
CWD_PATH = os.getcwd()
PATH_TO_IMG_DIR = os.path.join(CWD_PATH, 'img_folder', 'test.jpg')
s.connect((host, port))
#s.send(b'Hello Server!')
f = open(PATH_TO_IMG_DIR,'rb')
print('Sending...')
l = f.read(1024)
while (l):
print('Sending...')
s.send(l)
l = f.read(1024)
f.close()
print("Done Sending")
s.shutdown(socket.SHUT_WR)
print(s.recv(1024))
s.close # Close the socket when done
Using this code, I was able to transfer the photo from my Pi to my MacOS laptop.
Now, I used the code here as reference in order to transfer my photo from my Pi to my Android Device. Now, this is my code:
Server - Android
class ServerThread implements Runnable {
public void run() {
Socket socket;
try {
Log.e(TAG, "starting the serverthread at port 6001");
serverSocket = new ServerSocket(6001);
} catch (IOException e) {
Log.e(TAG, "exception in creating server socket: ", e);
e.printStackTrace();
}
while (!Thread.currentThread().isInterrupted()) {
try {
socket = serverSocket.accept();
CommunicationThread commThread = new CommunicationThread(socket);
new Thread(commThread).start();
} catch (IOException e) {
}
}
}
}
class CommunicationThread implements Runnable{
private Socket clientSocket;
private DataInputStream input;//private BufferedReader input;
public CommunicationThread(Socket clientSocket) {
this.clientSocket = clientSocket;
try {
Log.e(TAG, "getting data from the input stream!");
//this.input = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
InputStream in = this.clientSocket.getInputStream();
this.input = new DataInputStream(in);
} catch (IOException e) {
Log.e(TAG, "error in creating data input stream: ", e);
e.printStackTrace();
}
}
public void run() {
Log.e(TAG, "running the code!");
while (!Thread.currentThread().isInterrupted()) {
try {
Log.e(TAG, "parsing the input data stream!");
byte[] data;//String read = input.readLine();
int len= this.input.readInt();
if (len > 0) {
data = new byte[len];
this.input.readFully(data,0,data.length);
}
/*
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] data;
int length = 0;
while ((length = this.input.read(data))!=-1) {
out.write(data,0,length);
}
data=out.toByteArray();
*/
Log.e(TAG, "Updating the UI through a thread!!");
// updateConversationHandler.post(new updateUIThread(data));
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e(TAG, "error in reading sent data! ", e);
e.printStackTrace();
}
}
}
}
And in order to use these classes, I had declared the following as global variables:
Thread serverThread = null;
Handler updateConversationHandler;
And in my onCreate(), I have the following:
updateConversationHandler = new Handler();
this.serverThread = new Thread(new ServerThread());
this.serverThread.start();
The app would start, and the socket would be opened. However, when I attempt to send in the photo from my Pi, I hit an error at this block of code:
public void run() {
Log.e(TAG, "running the code!");
while (!Thread.currentThread().isInterrupted()) {
try {
Log.e(TAG, "parsing the input data stream!");
byte[] data;//String read = input.readLine();
int len= this.input.readInt();
if (len > 0) {
data = new byte[len];
this.input.readFully(data,0,data.length);
}
/*
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] data;
int length = 0;
while ((length = this.input.read(data))!=-1) {
out.write(data,0,length);
}
data=out.toByteArray();
*/
Log.e(TAG, "Updating the UI through a thread!!");
// updateConversationHandler.post(new updateUIThread(data));
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e(TAG, "error in reading sent data! ", e);
e.printStackTrace();
}
}
}
class updateUIThread implements Runnable {
private byte[] byteArray;//private String msg;
public updateUIThread(byte[] array){ //public updateUIThread(String str) {
this.byteArray=array; //this.msg = str;
}
#Override
public void run() {
Log.e(TAG, "running the photo update!");
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray , 0, byteArray .length);
ivBed.setImageBitmap(bitmap);//text.setText(text.getText().toString()+"Client Says: "+ msg + "\n");
}
}
Originally, the line:
data = new byte[len];
was outside the if(len>0) condition. But what happened was, for some reason, the Pi sent in a negative value, and of course we don't want a negative value for the len variable. Of course, I hit an error when I tried to create the byte array data with a negative length. I then put that line in the if condition.
However, after I did, I hit on OOM Error in the same line data = new byte[len];
Process: agict.work.freelance.patientsensor, PID: 15224
java.lang.OutOfMemoryError: Failed to allocate a 1677608328 byte allocation with 6291456 free bytes and 254MB until OOM, max allowed footprint 7797480, growth limit 268435456
I have a hunch that in the 2nd error, I was trying to initialize the byte array with a value that was actually the image data already, hence, the OOM error.
However, if I just take in the first value and assign it as the len, there's a chance that I'd get a negative number and the code would hit the first error.
Would there be a chance that I have to tweak something in order to transfer a photo data from Python3 to Android? I have a feeling that there's a format mismatch of sorts that's happening.
Again, my goal is to send a photo file from Python3 to my Android Device via TCP. The Python3 will be given a file, and the Android Device will decode the input it gets, and once decoded, use that data as the drawable for an ImageView.
According to me that error is caused by the size of the bitmap that you are trying to set for the ImageView. Checking sizes before use and/or scaling the bitmap may solve the issue. I've encountered such problems in the past before I start using third party libraries to load images.
You can take a look at this article from Android Developers and I think it can help:
https://developer.android.com/topic/performance/graphics/load-bitmap#java
I got it. You're supposed to use a ByteArrayOutputStream Object.
class ServerImageThread implements Runnable{
ServerSocket ss;
Socket s;
DataInputStream dis;
byte[] data;
#Override
public void run(){
try{
ss = new ServerSocket(6001);
while(true){
s = ss.accept();
InputStream in = s.getInputStream();
dis = new DataInputStream(in);
// read from the stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] content = new byte[1024];
int bytesRead = -1;
while( (bytesRead = in.read(content)) != -1 ) {
baos.write( content, 0, bytesRead );
} // while
Log.e(TAG, "made it through!");
Log.e(TAG, "baos size is = " + baos.size());
File file = new File(
Environment.getExternalStorageDirectory(), "test.jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
baos.writeTo(fos);
Log.e(TAG, "managed to write baos to fos");
} catch(IOException ioe) {
// Handle exception here
Log.e(TAG, "baos IOException = ", ioe);
ioe.printStackTrace();
} finally {
Log.e(TAG, "closing fos");
fos.close();
}
}
}
catch(IOException e) {
Log.e(TAG, "exception in creating server socket: ", e);
e.printStackTrace();
}
}
}
You can call this via:
Thread myThread = new Thread(new ServerImageThread());
myThread.start();

Writing a bluetooth input stream to a file skips data

I am trying to write an app that will log the output of an arduino Due to a text file on the phone or tablet. The output rate is 1kHz. I have based my app on the Blueserial code (https://github.com/plastygrove/BlueSerial). The bluetooth connection gets established properly with the arduino bluetooth module, the commands are sent and received properly and everything seems to work just fine. However, the file that I am saving the data to is missing blocks of data, usually around 200ms worth every so often (I have a millisecond timestamp included in my data), resulting in corrupted data. I have been trying to figure out the source of the problem and I think it might be related to the gc but at this point I am at a loss. This is the code that writes my data to the file:
private class ReadInput implements Runnable {
private boolean bStop = false;
private Thread t;
public ReadInput() {
t = new Thread(this, "Input Thread");
t.start();
}
public boolean isRunning() {
return t.isAlive();
}
public void run() {
InputStream inputStream;
try {
inputStream = mBTSocket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(inputStream);
while (!bStop) {
byte[] buffer = new byte[250];
int bytes = 0;
if (bis.available() > 0) {
bytes = bis.read(buffer);
strInput = new String(buffer, 0, bytes);
sb.append(strInput);
int endOfLineIndex = sb.indexOf("\r\n"); // determine the end-of-line
pw.print(strInput); // print buffer to the file buffer
pw.flush(); // flush buffer and force write to media
if (endOfLineIndex > 0) { // if end-of-line,
String sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length()); // and clear the string
pw.print(strInput); // write buffer to file buffer
pw.flush(); // force writing to file
pw.close(); // close print writer
try {
f.close(); // close file output stream
} catch (IOException e) {
e.printStackTrace();
}
}
sb.delete(0, sb.length()); strInput = "";
}
//Thread.sleep(100);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void stop() {
bStop = true;
}
}
and this is my file outputsteam and printwriter declarations:
String strInput = null;
static PrintWriter pw = null;
static FileOutputStream f = null;
private StringBuilder sb = new StringBuilder();
The data I am sending is formatted as so:
24.330,-58,5,119,460\n
24.331,-86,25,-105,460\n
24.332,66,41,-145,460\n
24.333,90,-23,-85,4622,-7,119,460\n
24.524,6,-95,107,461\n
24.525,10,-7,-173,461\n
24.526,-22,33,103,461\n
and in this example you can see where it skipped some data. Thank you for helping out!
There appears to be code missing or you have some extra logic.
For example the StringBuilder doesn't appear to be doing anything,
bStop is never set/cleared. But you are always printing out the incoming data
via strInput.
The end of line handling also looks off, specifically this:
String sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length());
You extract the string and then delete the whole buffer sb.delete(0,sb.length())
same at the bottom of the loop.

Unexpected status line: ICY 200 OK for URL openStream() method?

According to changes for kitakt 4.4 there were some problems with playing shoutcast streams (those returning "ICY" instead of "HTTP/1.x" response).
So solution for kitkat was to reregister "icy" protocol prefix in JVM once before we opened a stream by this:
try {
java.net.URL.setURLStreamHandlerFactory( new java.net.URLStreamHandlerFactory(){
public java.net.URLStreamHandler createURLStreamHandler( String protocol ) {
Log.d( LOG, "Asking for stream handler for protocol: '" + protocol + "'" );
if ("icy".equals( protocol )) return new com.spoledge.aacdecoder.IcyURLStreamHandler();
return null;
}
});
}
catch (Throwable t) {
Log.w( LOG, "Cannot set the ICY URLStreamHandler - maybe already set ? - " + t );
}
I have problem with open audio stream to make it register. After I call url.opnestream(stream) I got exception:
java.net.ProtocolException: Unexpected status line: ICY 200 OK
How could I fix it?
Here is sample of registering audio, so far what I did..
try {
URL url = null;
url = new URL(u);
inputStream = url.openStream();
startTime = System.currentTimeMillis();
Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
String fileName = File.separator + "radio_" + "recording_" + channelMetadata.replaceAll("\\W", "") + System.currentTimeMillis();
if(isSDPresent)
{
outputSource = Environment.getExternalStorageDirectory() + fileName;
}
else
{
outputSource = Environment.getDataDirectory() + fileName;
}
if(contentType.equals("audio/aacp"))
fileOutputStream = new FileOutputStream(outputSource + ".acc");
else if(contentType.equals("audio/mpeg"))
fileOutputStream = new FileOutputStream(outputSource + ".mp3");
else
fileOutputStream = new FileOutputStream(outputSource + ".nieznany_format");
int bytesRead = 0;
int bytes;
while (((bytes = inputStream.read()) != -1) && isRecording) {
fileOutputStream.write(bytes);
bytesRead++;
stopTime = System.currentTimeMillis();
long seconds = (Math.abs(startTime-stopTime));
int minutes = 1000 * 60 * 60;
if(minutes<=seconds)
{
Log.d("xxx", "recording task exceed stopped");
break;
}
}
inputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
isRecording = false;
}
isRecording = false;
return null;
Various changes were made for Android 4.4, and it seems that the non-standard ShoutCast ICY header is not supported by Android.
It seems though that the good people of OkHttp fixed the issue (issue 1 and issue 2) already a few days ago.
What you can do, is simply use OkHttp lib directly in your application and by that you'll use the newer OkHttp version (the one with the fix) and not the one shipped with Android (where you'll have to wait for an OS update).
This will also fix it for your application running on other devices that might suffer from that issue.
The answer of Assaf Gamliel worked for me. For those not familiar with this, you have to download the last .jar from okHttp and the .jar from the dependency okio . Add them to your libs directory and connect like this :
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(mediaUrl)
.build();
Response response = client.newCall(request).execute();
InputStream stream = response.body().byteStream();
The easiest fix ever.
which is 100% working for me,
for shoutcast , change your URL ex. "http://192.168.1.1:9292" to "icy://192.168.1.1:9292"
and you'll be fine.

Error to send data to tablet through bluetooth device

I'm facing the following problem:
I am connecting two devices via Bluetooth socket, one tablet android and a bluetooth device like reader barcode, up to now it's ok, the problem is, when a read the barcode by the bluetooth device and I send it to tablet, the bar code sometimes it's sent in two parts, for example, if I read a barcode with content "212154521212", the tablet receive "2121" and after "54521212", Anyone know tell me what should I do to avoid this?
Thanks in advanced.
My code that read the data from bluetooth device:
[code]
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private BluetoothSocket socket;
public ConnectedThread(BluetoothSocket socket) {
this.socket = socket;
InputStream tmpIn = null;
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
new LogDeErrosRodesTablet(e);
Log.e(TAG, e.getMessage());
Log.e(TAG, "Erro no construtor da classe ConnectedThread.");
}
mmInStream = tmpIn;
}
public void run() {
// continua lendo o inputstream até ocorrer um erro
while (true) {
int read = 0;
byte[] buffer = new byte[128];
do {
try {
read = mmInStream.read(buffer);
Log.e(TAG, "read: " + read);
final String data = new String(buffer, 0, read);
Log.e(TAG, "data: " + data);
//TODO
//send data only (bar code) only after read all
Bundle bundle = new Bundle();
bundle.putString(TelaInserirPedido.CODIGO_BARRAS, data);
Message message = new Message();
message.what = TelaInserirPedido.MSG_COD_BARRAS;
message.setData(bundle);
//Send a message with data
handler.sendMessage(message);
} catch(Exception ex) {
read = -1;
return;
}
Log.e(TAG, "inside while.");
} while (read > 0);
Log.e(TAG, "outside of while.");
}
}
public void cancel () {
try {
socket.close ();
} catch ( IOException e) { }
}
}
[/code]
This isn't a Bluetooth error. The Bluetooth device is sending all of the data to your application, but you are reading the stream before all of the data have been received. You could check for the amount of bytes available() on the stream before reading, if you know the exact length of the data; you could concatenate the results of all of the reads until you reach a known end point. Or you could put in an arbitrary time delay and hope the transmission completed in that time.
You would create the Bundle and Message after the while loop that collects the input string, because you don't know the entire string until that loop finishes. (Unless you are expecting multiple strings in one connection, in which case you need more complex code to handle partial numbers).
Use OutputStream.flush() to force send the all data.

SSLProtocolException when reading https responses on Android 2.3.3 devices

I'm facing a problem with a bug (Issue 16121) that was introduced in Gingerbread 2.3.3 and fixed with 2.3.4.
Reading the response of a https request throws an SSLProtocolException after reading ~40kB from the inputstream. The problem is described at Issue 16121. At the bottom of the page is a android project that reveals the bug. The bug report originates from user 'Alex' on stackoverflow (question).
I have an app in the market that sends many different https requests. Most of them need to receive up to 200kB. I cannot change anything on the server side. I use the DefaultHttpClient to send https requests.
How can I efficiently solve that problem?
Using a different https api? Which https api?
Here are some sample methods, as noted in the comment, I am using CharTerminatedInputStream, under the Apache license. The URL to see it is in the comment as well.
private static final int BUFFER_SIZE = 4096;
private static final char[] TERMINATOR = new char[]{'\r', '\n', '\r', '\n'};
/**
* Simple SSL connect example to avoid Issue 15356 on Android 2.3.3
*
* #param host The host/server name
* #param port The TCP port to use (443 is default for HTTP over SSL)
* #param file The file you are requesting (/path/to/file/on/server.doc)
* #param fileOut Your <code>OutputStream</code> for the file you are writing to
* #throws Exception If any error occurs - obviously should be improved for your implementation
*/
private static void downloadFileOverSSL(String host, int port, String file, OutputStream fileOut) throws Exception {
PrintWriter socketOut = null;
InputStream socketIn = null;
try {
// create a socket to talk to the server on
SocketFactory factory = SSLSocketFactory.getDefault();
Socket socket = factory.createSocket(host, port);
// we'll use this to send our request to the server
socketOut = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
//This is what Java was sending using URLConnection, and it works here too...
// You can always change this to something both your app and server will understand depending how it is setup
// This is the least you need in the request:
/*String requestStr = "GET " + file + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"\r\n";*/
String requestStr = "GET " + file + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"User-Agent: Java/1.6.0_25\r\n" +
"Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" +
"Connection: keep-alive\r\n" +
"\r\n";
//Log.i(getLogTag(), "Request being sent: `" + requestStr + "\"");
// send the request to the server
socketOut.print(requestStr);
socketOut.flush();
// this reads the server's response
socketIn = socket.getInputStream();
/*
Write the results into our local file's output stream
*/
// This is the tricky part, the raw socket returns the HTTP 200 response and headers.
// This can probably be optimized, but it's just reading through until it finds \r\n\r\n
// You can use something like CharTerminatedInputStream
// (ref: http://www.java2s.com/Tutorial/Java/0180__File/AnInputStreamclassthatterminatesthestreamwhenitencountersaparticularbytesequence.htm)
CharTerminatedInputStream charTermInput = new CharTerminatedInputStream(socketIn, TERMINATOR);
while (charTermInput.read() != -1) {
// -1 indicates a match was made, IOException or ProtocolException thrown if match not made by end of stream
}
int numBytesRead;
byte[] buffer = new byte[BUFFER_SIZE];
while ((numBytesRead = socketIn.read(buffer, 0, BUFFER_SIZE)) != -1) {
fileOut.write(buffer, 0, numBytesRead);
//Log.d(getLogTag(), "Reading data [" + numBytesRead + "]: " + new String(buffer, 0, numBytesRead));
}
fileOut.flush();
} finally {
safeClose(socketOut);
safeClose(socketIn);
safeClose(fileOut);
}
}
private static void safeClose(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ioe) {
//Log.w(getLogTag(), "Failed to close stream", ioe);
}
}
}

Categories

Resources