Implementation of Xmodem protocol in Java - java

How would I receive a file over a serial port in Java using the XMODEM protocol?

Here it is.
I found this in the JModem source. If you look at where it writes the data out, you can see its doing an SOH, blocknum, ~blocknum, data, and checksum. It uses a sector size of 128. Those together make up the standard XModem protocol. Its simple enough to do XModem1K, YModem, and ZModem from here, too.
/**
* a tiny version of Ward Christensen's MODEM program for UNIX.
* Written ~ 1980 by Andrew Scott Beals. Last revised 1982.
* A.D. 2000 - dragged from the archives for use in Java Cookbook.
*
* #author C version by Andrew Scott Beals, sjobrg.andy%mit-oz#mit-mc.arpa.
* #author Java version by Ian F. Darwin, ian#darwinsys.com
* $Id: TModem.java,v 1.8 2000/03/02 03:40:50 ian Exp $
*/
class TModem {
protected final byte CPMEOF = 26; /* control/z */
protected final int MAXERRORS = 10; /* max times to retry one block */
protected final int SECSIZE = 128; /* cpm sector, transmission block */
protected final int SENTIMOUT = 30; /* timeout time in send */
protected final int SLEEP = 30; /* timeout time in recv */
/* Protocol characters used */
protected final byte SOH = 1; /* Start Of Header */
protected final byte EOT = 4; /* End Of Transmission */
protected final byte ACK = 6; /* ACKnowlege */
protected final byte NAK = 0x15; /* Negative AcKnowlege */
protected InputStream inStream;
protected OutputStream outStream;
protected PrintWriter errStream;
/** Construct a TModem */
public TModem(InputStream is, OutputStream os, PrintWriter errs) {
inStream = is;
outStream = os;
errStream = errs;
}
/** Construct a TModem with default files (stdin and stdout). */
public TModem() {
inStream = System.in;
outStream = System.out;
errStream = new PrintWriter(System.err);
}
/** A main program, for direct invocation. */
public static void main(String[] argv) throws
IOException, InterruptedException {
/* argc must == 2, i.e., `java TModem -s filename' */
if (argv.length != 2)
usage();
if (argv[0].charAt(0) != '-')
usage();
TModem tm = new TModem();
tm.setStandalone(true);
boolean OK = false;
switch (argv[0].charAt(1)){
case 'r':
OK = tm.receive(argv[1]);
break;
case 's':
OK = tm.send(argv[1]);
break;
default:
usage();
}
System.out.print(OK?"Done OK":"Failed");
System.exit(0);
}
/* give user minimal usage message */
protected static void usage()
{
System.err.println("usage: TModem -r/-s file");
// not errStream, not die(), since this is static.
System.exit(1);
}
/** If we're in a standalone app it is OK to System.exit() */
protected boolean standalone = false;
public void setStandalone(boolean is) {
standalone = is;
}
public boolean isStandalone() {
return standalone;
}
/** A flag used to communicate with inner class IOTimer */
protected boolean gotChar;
/** An inner class to provide a read timeout for alarms. */
class IOTimer extends Thread {
String message;
long milliseconds;
/** Construct an IO Timer */
IOTimer(long sec, String mesg) {
milliseconds = 1000 * sec;
message = mesg;
}
public void run() {
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
// can't happen
}
/** Implement the timer */
if (!gotChar)
errStream.println("Timed out waiting for " + message);
die(1);
}
}
/*
* send a file to the remote
*/
public boolean send(String tfile) throws IOException, InterruptedException
{
char checksum, index, blocknumber, errorcount;
byte character;
byte[] sector = new byte[SECSIZE];
int nbytes;
DataInputStream foo;
foo = new DataInputStream(new FileInputStream(tfile));
errStream.println( "file open, ready to send");
errorcount = 0;
blocknumber = 1;
// The C version uses "alarm()", a UNIX-only system call,
// to detect if the read times out. Here we do detect it
// by using a Thread, the IOTimer class defined above.
gotChar = false;
new IOTimer(SENTIMOUT, "NAK to start send").start();
do {
character = getchar();
gotChar = true;
if (character != NAK && errorcount < MAXERRORS)
++errorcount;
} while (character != NAK && errorcount < MAXERRORS);
errStream.println( "transmission beginning");
if (errorcount == MAXERRORS) {
xerror();
}
while ((nbytes=inStream.read(sector))!=0) {
if (nbytes<SECSIZE)
sector[nbytes]=CPMEOF;
errorcount = 0;
while (errorcount < MAXERRORS) {
errStream.println( "{" + blocknumber + "} ");
putchar(SOH); /* here is our header */
putchar(blocknumber); /* the block number */
putchar(~blocknumber); /* & its complement */
checksum = 0;
for (index = 0; index < SECSIZE; index++) {
putchar(sector[index]);
checksum += sector[index];
}
putchar(checksum); /* tell our checksum */
if (getchar() != ACK)
++errorcount;
else
break;
}
if (errorcount == MAXERRORS)
xerror();
++blocknumber;
}
boolean isAck = false;
while (!isAck) {
putchar(EOT);
isAck = getchar() == ACK;
}
errStream.println( "Transmission complete.");
return true;
}
/*
* receive a file from the remote
*/
public boolean receive(String tfile) throws IOException, InterruptedException
{
char checksum, index, blocknumber, errorcount;
byte character;
byte[] sector = new byte[SECSIZE];
DataOutputStream foo;
foo = new DataOutputStream(new FileOutputStream(tfile));
System.out.println("you have " + SLEEP + " seconds...");
/* wait for the user or remote to get his act together */
gotChar = false;
new IOTimer(SLEEP, "receive from remote").start();
errStream.println("Starting receive...");
putchar(NAK);
errorcount = 0;
blocknumber = 1;
rxLoop:
do {
character = getchar();
gotChar = true;
if (character != EOT) {
try {
byte not_ch;
if (character != SOH) {
errStream.println( "Not SOH");
if (++errorcount < MAXERRORS)
continue rxLoop;
else
xerror();
}
character = getchar();
not_ch = (byte)(~getchar());
errStream.println( "[" + character + "] ");
if (character != not_ch) {
errStream.println( "Blockcounts not ~");
++errorcount;
continue rxLoop;
}
if (character != blocknumber) {
errStream.println( "Wrong blocknumber");
++errorcount;
continue rxLoop;
}
checksum = 0;
for (index = 0; index < SECSIZE; index++) {
sector[index] = getchar();
checksum += sector[index];
}
if (checksum != getchar()) {
errStream.println( "Bad checksum");
errorcount++;
continue rxLoop;
}
putchar(ACK);
blocknumber++;
try {
foo.write(sector);
} catch (IOException e) {
errStream.println("write failed, blocknumber " + blocknumber);
}
} finally {
if (errorcount != 0)
putchar(NAK);
}
}
} while (character != EOT);
foo.close();
putchar(ACK); /* tell the other end we accepted his EOT */
putchar(ACK);
putchar(ACK);
errStream.println("Receive Completed.");
return true;
}
protected byte getchar() throws IOException {
return (byte)inStream.read();
}
protected void putchar(int c) throws IOException {
outStream.write(c);
}
protected void xerror()
{
errStream.println("too many errors...aborting");
die(1);
}
protected void die(int how)
{
if (standalone)
System.exit(how);
else
System.out.println(("Error code " + how));
}
}

Related

Signature generation with OpenSC Smart Card

I have Escrypt Smart Card to sign data bytes and get signature and certificate for it.
I have java tool to do it and everything was fine until Java 8. Now application migrated to Java 11. And problem start from here.
SunPKCS11.jar/ library is not a part of Java 11, hence application breaks.
I tried with different solution available over internet and oracle release notes, it suggest to use java.security package for cryptographic operation.
I am not able to switch from Java 8 to java 11, any support would be really useful.
Edit-1: Minimum usable code
Signature calculator is used to parse signature
It is written with Java 8 & SunPKCS11.jar want to migrate it to Java 11
'''
import java.io.IOException;
import java.util.Scanner;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import sun.security.pkcs11.wrapper.CK_ATTRIBUTE;
import sun.security.pkcs11.wrapper.CK_MECHANISM;
import sun.security.pkcs11.wrapper.CK_RSA_PKCS_PSS_PARAMS;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Constants;
import sun.security.pkcs11.wrapper.PKCS11Exception;
public class Application {
public static void main(String[] args) {
try {
// PKCS11 middleware (UBK PKI) path
String libraryPath = "";
OpenScPkcs11 openScPkcs11 = new OpenScPkcs11(libraryPath);
openScPkcs11.login("", "");
byte[] hash = null;
String keypath = null;
String oid = null;
String authbits = null;
openScPkcs11.calcSign(hash, keypath, oid, authbits);
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class OpenScPkcs11 {
private static HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter();
private int slotId = 0x3;
private String libraryPath;
private PKCS11 pkcs11Instance;
private String signatureAlgo = "RSA";
private long session = -1;
private boolean isLoginDone = false;
private SignatureMechanism.Algorithms algorithm = SignatureMechanism.Algorithms.SHA256;
public OpenScPkcs11(String libraryPath) throws IOException, PKCS11Exception {
this.libraryPath = libraryPath;
initializeMiddleware();
}
public void calcSign(byte[] hash, String keyPath, String userOid, String authbits) throws Exception {
byte[] signature = new byte[512];
if (this.pkcs11Instance != null) {
if (this.session < 0) {
this.openSession();
}
if (!this.isLoginDone) {
this.login("", "");
}
Mechanism mechanism = SignatureMechanism.getMechanism(this.algorithm);
CK_ATTRIBUTE[] pTemplate = new CK_ATTRIBUTE[3];
pTemplate[0] = new CK_ATTRIBUTE(PKCS11Constants.CKA_CLASS, PKCS11Constants.CKO_PRIVATE_KEY);
pTemplate[1] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VENDOR_DEFINED + 1, keyPath);
pTemplate[2] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VENDOR_DEFINED + 2, authbits);
// define the attibutes for certificate
CK_ATTRIBUTE[] certAttribute = new CK_ATTRIBUTE[1];
certAttribute[0] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VENDOR_DEFINED + 3);
long[] c_FindObjects = null;
this.pkcs11Instance.C_FindObjectsInit(this.session, pTemplate);
c_FindObjects = this.pkcs11Instance.C_FindObjects(this.session, 32);
this.pkcs11Instance.C_FindObjectsFinal(this.session);
CK_MECHANISM pMechanism = null;
// RSA Algorithm
String signatureAlgorithmType = this.getSignatureAlgorithmType();
if (signatureAlgorithmType.equalsIgnoreCase("RSA")) {
pMechanism = new CK_MECHANISM(mechanism.getId());
CK_RSA_PKCS_PSS_PARAMS ck_RSA_PKCS_PSS_PARAMS =
new CK_RSA_PKCS_PSS_PARAMS(this.algorithm.name(), "MGF1", this.algorithm.name(), (int) mechanism.getsLen());
pMechanism.pParameter = ck_RSA_PKCS_PSS_PARAMS;
}
else if (signatureAlgorithmType.equalsIgnoreCase("ECDSA")) { // ECDSA Algorithm
pMechanism = new CK_MECHANISM(PKCS11Constants.CKM_ECDSA);
}
else {
throw new Exception("Signature algorithm " + signatureAlgorithmType + " is not supported");
}
if ((c_FindObjects != null) && (c_FindObjects.length > 0)) {
long c_FindObjectFound = c_FindObjects[0];
boolean objFound = false;
for (long c_FindObject : c_FindObjects) {
this.pkcs11Instance.C_GetAttributeValue(this.session, c_FindObject, certAttribute);
// Binary certificate as byte array
byte[] certificateBytes = certAttribute[0].getByteArray();
if ((userOid != null) && !userOid.isEmpty()) {
// Match certificate with userOid, if matches (Certificate parser used)
if (parseOidInfo(certificateBytes, userOid)) {
c_FindObjectFound = c_FindObject;
objFound = true;
break;
}
}
}
if (objFound) {
System.out.println("Signature found for given OID configuration.");
}
else {
this.pkcs11Instance.C_GetAttributeValue(this.session, c_FindObjectFound, certAttribute);
CertificateParser certificateParser = new CertificateParser(certAttribute[0].getByteArray());
}
this.pkcs11Instance.C_SignInit(this.session, pMechanism, c_FindObjectFound);
this.pkcs11Instance.C_SignUpdate(this.session, 0, hash, 0, (int) mechanism.getsLen());
signature = this.pkcs11Instance.C_SignFinal(this.session, mechanism.getSignFinalArgument());
}
else {
String errorMessage = "Unable to find keys.";
throw new Exception(errorMessage);
}
}
else {
throw new Exception("Initialize middleware first.");
}
}
/**
* #return
*/
private String getSignatureAlgorithmType() {
return this.signatureAlgo;
}
public void login(String userName, String password) throws Exception {
if (this.pkcs11Instance != null) {
openSession();
String pwd = password;
if (pwd == null || pwd.trim().isEmpty()) {
Scanner sc = new Scanner(System.in);
pwd = sc.next();
sc.close();
}
this.pkcs11Instance.C_Login(this.session, PKCS11Constants.CKU_USER, pwd.toCharArray());
this.isLoginDone = true;
}
else {
throw new Exception("Initialize middleware first.");
}
}
public void logout() throws PKCS11Exception {
if (this.pkcs11Instance != null) {
this.pkcs11Instance.C_Logout(this.session);
}
}
public void openSession() throws Exception {
if (this.pkcs11Instance != null) {
if (this.session < 0) {
long[] c_GetSlotList = this.pkcs11Instance.C_GetSlotList(true);
if ((c_GetSlotList != null) && (c_GetSlotList.length > 0)) {
for (long element : c_GetSlotList) {
if (element == this.slotId) {
this.session =
this.pkcs11Instance.C_OpenSession(this.slotId, PKCS11Constants.CKF_SERIAL_SESSION, null, null);
break;
}
}
}
}
}
else {
throw new Exception("Initialize middleware first.");
}
}
public void closeSession() throws PKCS11Exception {
if ((this.pkcs11Instance != null) && (this.session >= 0)) {
this.pkcs11Instance.C_CloseSession(this.session);
this.session = -1;
}
}
public void initializeMiddleware(String libraryPath1) throws IOException, PKCS11Exception {
this.pkcs11Instance = PKCS11.getInstance(libraryPath1, "C_GetFunctionList", null, false);
this.libraryPath = libraryPath1;
}
public void initializeMiddleware() throws IOException, PKCS11Exception {
this.pkcs11Instance = PKCS11.getInstance(this.libraryPath, "C_GetFunctionList", null, false);
}
public static String getString(final byte[] data) {
if ((data != null) && (data.length > 0)) {
return hexBinaryAdapter.marshal(data);
}
return null;
}
}
class SignatureMechanism {
public static enum Algorithms {
SHA256(0x250),
/**
* Hash calculation Algorithm
*/
RIPEMD160(0x240),
/**
* Hash calculation Algorithm
*/
RIPEMD160_1(0x0),
/**
* Secure Hash Algorithm 512 for hash Calculation
*/
SHA512(0x251);
private int value = 0;
private Algorithms(final int algorithmValue) {
this.value = algorithmValue;
}
/**
* #return the hash value
*/
public int getValue() {
return this.value;
}
}
/**
* #param algorithm : Algorithm used for hash calculation
* #return signature mechanism
*/
public static Mechanism getMechanism(final Algorithms algorithm) {
Mechanism mechanism = new Mechanism();
if (algorithm == Algorithms.SHA256) {
mechanism.setHashAlg(PKCS11Constants.CKM_SHA256);
mechanism.setMgf(PKCS11Constants.CKG_MGF1_SHA1 + 1);
mechanism.setsLen(32);
mechanism.setSignFinalArgument(512);
} // TODO Verify with ETAS Middleware team
else if (algorithm == Algorithms.SHA512) {
mechanism.setHashAlg(PKCS11Constants.CKM_SHA512);
mechanism.setMgf(PKCS11Constants.CKG_MGF1_SHA1 + 1);
mechanism.setsLen(64);
mechanism.setSignFinalArgument(1024);
}
else if (algorithm == Algorithms.RIPEMD160) {
mechanism.setHashAlg(PKCS11Constants.CKM_RIPEMD160);
mechanism.setMgf(0x80000001); // hard coded becuase it is defined by escrypt and not present in PKCS11
mechanism.setsLen(20);
}
else if (algorithm == Algorithms.RIPEMD160_1) {
mechanism.setId(PKCS11Constants.CKM_RIPEMD160_RSA_PKCS);
}
return mechanism;
}
}
class Mechanism{
private long hashAlg;
private long mgf;
private long sLen;
private long ulMaxObjectCount = 32;
private int signFinalArgument = 128;
private long id = PKCS11Constants.CKM_RSA_PKCS_PSS;
/**
* #return the hashAlg
*/
public long getHashAlg() {
return hashAlg;
}
/**
* #param hashAlg the hashAlg to set
*/
public void setHashAlg(long hashAlg) {
this.hashAlg = hashAlg;
}
/**
* #return the mgf
*/
public long getMgf() {
return mgf;
}
/**
* #param mgf the mgf to set
*/
public void setMgf(long mgf) {
this.mgf = mgf;
}
/**
* #return the sLen
*/
public long getsLen() {
return sLen;
}
/**
* #param sLen the sLen to set
*/
public void setsLen(long sLen) {
this.sLen = sLen;
}
/**
* #return the ulMaxObjectCount
*/
public long getUlMaxObjectCount() {
return ulMaxObjectCount;
}
/**
* #param ulMaxObjectCount the ulMaxObjectCount to set
*/
public void setUlMaxObjectCount(long ulMaxObjectCount) {
this.ulMaxObjectCount = ulMaxObjectCount;
}
/**
* #return the signFinalArgument
*/
public int getSignFinalArgument() {
return signFinalArgument;
}
/**
* #param signFinalArgument the signFinalArgument to set
*/
public void setSignFinalArgument(int signFinalArgument) {
this.signFinalArgument = signFinalArgument;
}
/**
* #return the id
*/
public long getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(long id) {
this.id = id;
}
}
'''
Thank You!
Java 9 up no longer puts the standard-library in jar files (rt.jar, etc) but they are still there. Your problem is probably (though I didn't read through all the code and certainly can't test without your hardware) that Java 9 up also introduces modules. Much as in Java forever your code can reference another class or interface (or static member therof) by its simple name only if you import it or it is in the same package as your code (if any) or the special package java.lang, now in 9 up you can only access a package if it is in the same module as your code or the special java.base module or your module and/or the other module explicitly allow the access; the explicit specification closet to older Java is an 'open'.
Thus the quick solution is to add to your java command (explicitly or via _JAVA_OPTS or equivalent) --add-opens jdk.crypto.cryptoki/sun.security.pkcs11.*=ALL-UNNAMED -- this should produce substantially the same result as running on 8 (or lower).
The official solution is to put your code in a module -- either by itself or with other related code -- that requires the (or each) needed module, given it exports the needed parts, which I haven't checked for yours. However, since your code apparently isn't even in a package yet, putting it in a module will be some work.

Send all contacts to mail as CSV file using Android code

Hello Every one here the below code is for sending all contacts to mail in CSV file format
This is my CSV sender class
public class CsvSender extends Activity {
private Cursor cursor;
private boolean csv_status = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_csv_sender);
createCSV();
if(csv_status)
exportCSV();
}
private void createCSV() {
CSVWriter writer = null;
try {
writer = new CSVWriter(new FileWriter(Environment.getExternalStorageDirectory().getAbsolutePath() + "/my_test_contact.csv"));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String displayName;
String number;
long _id;
String columns[] = new String[]{ ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME };
writer.writeColumnNames(); // Write column header
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
columns,
null,
null,
ContactsContract.Data.DISPLAY_NAME +" COLLATE LOCALIZED ASC" );
startManagingCursor(cursor);
if(cursor.moveToFirst()) {
do {
_id = Long.parseLong(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)));
displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)).trim();
number = getPrimaryNumber(_id);
writer.writeNext((displayName + "/" + number).split("/"));
} while(cursor.moveToNext());
csv_status = true;
} else {
csv_status = false;
}
try {
if(writer != null)
writer.close();
} catch (IOException e) {
Log.w("Test", e.toString());
}
}// Method close.
private void exportCSV() {
if(csv_status == true) {
//CSV file is created so we need to Export that ...
final File CSVFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/my_test_contact.csv");
//Log.i("SEND EMAIL TESTING", "Email sending");
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/csv");
emailIntent .putExtra(android.content.Intent.EXTRA_SUBJECT, "Test contacts ");
emailIntent .putExtra(android.content.Intent.EXTRA_TEXT, "\n\nAdroid developer\n Sathish");
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + CSVFile.getAbsolutePath()));
emailIntent.setType("message/rfc822"); // Shows all application that supports SEND activity
try {
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(getApplicationContext(), "Email client : " + ex.toString(), Toast.LENGTH_SHORT);
}
} else {
Toast.makeText(getApplicationContext(), "Information not available to create CSV.", Toast.LENGTH_SHORT).show();
}
}
/**
* Get primary Number of requested id.
*
* #return string value of primary number.
*/
private String getPrimaryNumber(long _id) {
String primaryNumber = null;
try {
Cursor cursor = getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{NUMBER, ContactsContract.CommonDataKinds.Phone.TYPE},
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ _id, // We need to add more selection for phone type
null,
null);
if(cursor != null) {
while(cursor.moveToNext()){
switch(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE))){
case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE :
primaryNumber = cursor.getString(cursor.getColumnIndex(NUMBER));
break;
case ContactsContract.CommonDataKinds.Phone.TYPE_HOME :
primaryNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
break;
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK :
primaryNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
break;
case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER :
}
if(primaryNumber != null)
break;
}
}
} catch (Exception e) {
Log.i("test", "Exception " + e.toString());
} finally {
if(cursor != null) {
cursor.deactivate();
cursor.close();
}
}
return primaryNumber;
}
}
This is my CSV writer class
/**
* Created by ADMIN on 23-01-2018.
*/
class CSVWriter {
private PrintWriter pw;
private char separator;
private char quotechar;
private char escapechar;
private String lineEnd;
/** The character used for escaping quotes. */
public static final char DEFAULT_ESCAPE_CHARACTER = '"';
/** The default separator to use if none is supplied to the constructor. */
public static final char DEFAULT_SEPARATOR = ',';
/**
* The default quote character to use if none is supplied to the
* constructor.
*/
public static final char DEFAULT_QUOTE_CHARACTER = '"';
/** The quote constant to use when you wish to suppress all quoting. */
public static final char NO_QUOTE_CHARACTER = '\u0000';
/** The escape constant to use when you wish to suppress all escaping. */
public static final char NO_ESCAPE_CHARACTER = '\u0000';
/** Default line terminator uses platform encoding. */
public static final String DEFAULT_LINE_END = "\n";
/** Default column name. */
public static final String DEFAULT_COLUMN_NAME = "Contact Name,Phone Number,";
/**
* Constructs CSVWriter using a comma for the separator.
*
* #param writer
* the writer to an underlying CSV source.
*/
public CSVWriter(Writer writer) {
this(writer, DEFAULT_SEPARATOR, DEFAULT_QUOTE_CHARACTER,
DEFAULT_ESCAPE_CHARACTER, DEFAULT_LINE_END);
}
/**
* Constructs CSVWriter with supplied separator, quote char, escape char and line ending.
*
* #param writer
* the writer to an underlying CSV source.
* #param separator
* the delimiter to use for separating entries
* #param quotechar
* the character to use for quoted elements
* #param escapechar
* the character to use for escaping quotechars or escapechars
* #param lineEnd
* the line feed terminator to use
*/
public CSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
this.pw = new PrintWriter(writer);
this.separator = separator;
this.quotechar = quotechar;
this.escapechar = escapechar;
this.lineEnd = lineEnd;
}
/**
* Writes the next line to the file.
*
* #param nextLine
* a string array with each comma-separated element as a separate
* entry.
*/
public void writeNext(String[] nextLine) {
if (nextLine == null)
return;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < nextLine.length; i++) {
if (i != 0) {
sb.append(separator);
}
String nextElement = nextLine[i];
if (nextElement == null)
continue;
if (quotechar != NO_QUOTE_CHARACTER)
sb.append(quotechar);
for (int j = 0; j < nextElement.length(); j++) {
char nextChar = nextElement.charAt(j);
if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) {
sb.append(escapechar).append(nextChar);
} else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) {
sb.append(escapechar).append(nextChar);
} else {
sb.append(nextChar);
}
}
if (quotechar != NO_QUOTE_CHARACTER)
sb.append(quotechar);
}
sb.append(lineEnd);
pw.write(sb.toString());
}
public void writeColumnNames() {
writeNext(DEFAULT_COLUMN_NAME.split(","));
}
/**
* Flush underlying stream to writer.
*
* #throws IOException if bad things happen
*/
public void flush() throws IOException {
pw.flush();
}
/**
* Close the underlying stream writer flushing any buffered content.
*
* #throws IOException if bad things happen
*
*/
public void close() throws IOException {
pw.flush();
pw.close();
}
}
Here I'm able to send my contacts but the mobile numbers which are starting with `+91 are display like (9.1903E+11). How can I solve this please help anyone.
Thanks in advance.

Why my app stops downloading always the same file at 5-10% of progress? (Only on a few machines)

My app seems to work on most of the machines, but some just don't want to cooperate... The files on the server are fine, server itself isn't down or anything. The problem is that the app downloads all required files (up to 600KB) correctly except one (12MB). I can't figure out what is wrong. My guess is that there's something running in background that blocks the downloading thread of my app, or maybe the router blocks some of the packets? All I can see is that progress bar goes up to 4% (sometimes even up to 8!) and then stops with no errors or exceptions, while all the other downloads go up to 100% without problems. Any ideas?
Here's the class I use to download files (I've downloaded the whole 'download manager' script from http://www.java-tips.org/java-se-tips/javax.swing/how-to-create-a-download-manager-in-java.html):
class Download extends Observable implements Runnable {
private static final int MAX_BUFFER_SIZE = 1024;
public static final String STATUSES[] = { "Pobieranie", "Pauza", "OK", "Anulowany",
"Błąd" };
public static final int DOWNLOADING = 0;
public static final int PAUSED = 1;
public static final int COMPLETE = 2;
public static final int CANCELLED = 3;
public static final int ERROR = 4;
private URL url; // download URL
private int size; // size of download in bytes
private int downloaded; // number of bytes downloaded
private int status; // current status of download
// Constructor for Download.
public Download(URL url) {
this.url = url;
size = -1;
downloaded = 0;
status = DOWNLOADING;
System.err.println("==========Pobieram plik: " + url.toString());
// Begin the download.
download();
}
// Get this download's URL.
public String getUrl() {
//return url.toString();
return getFileName(url);
}
// Get this download's size.
public int getSize() {
return size;
}
// Get this download's progress.
public float getProgress() {
return ((float) downloaded / size) * 100;
}
public int getStatus() {
return status;
}
public void pause() {
status = PAUSED;
stateChanged();
}
public void resume() {
status = DOWNLOADING;
stateChanged();
download();
}
public void cancel() {
status = CANCELLED;
stateChanged();
}
private void error() {
status = ERROR;
stateChanged();
}
private void download() {
Thread thread = new Thread(this);
thread.start();
}
// Get file name portion of URL.
private String getFileName(URL url) {
String fileName = url.getFile();
return fileName.substring(fileName.lastIndexOf('/') + 1);
}
// Download file.
public void run() {
RandomAccessFile file = null;
InputStream stream = null;
try {
// Open connection to URL.
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Specify what portion of file to download.
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
// Connect to server.
connection.connect();
// Make sure response code is in the 200 range.
if (connection.getResponseCode() / 100 != 2) {
error();
}
// Check for valid content length.
int contentLength = connection.getContentLength();
if (contentLength < 1) {
error();
}
/*
* Set the size for this download if it hasn't been already set.
*/
if (size == -1) {
size = contentLength;
stateChanged();
}
File f = new File(Files.download_dir + Files.SEPARATOR + getFileName(url));
if (f.exists()) f.delete();
// Open file and seek to the end of it.
file = new RandomAccessFile(Files.download_dir + Files.SEPARATOR + getFileName(url), "rw");
file.seek(downloaded);
stream = connection.getInputStream();
while (status == DOWNLOADING) {
/*
* Size buffer according to how much of the file is left to download.
*/
byte buffer[];
if (size - downloaded > MAX_BUFFER_SIZE) {
buffer = new byte[MAX_BUFFER_SIZE];
} else {
buffer = new byte[size - downloaded];
}
// Read from server into buffer.
int read = stream.read(buffer);
if (read == -1)
break;
// Write buffer to file.
file.write(buffer, 0, read);
downloaded += read;
//System.out.println("Pobralem juz: " + String.valueOf(downloaded));
stateChanged();
}
/*
* Change status to complete if this point was reached because downloading
* has finished.
*/
if (status == DOWNLOADING) {
status = COMPLETE;
stateChanged();
}
} catch (Exception e) {
error();
} finally {
// Close file.
if (file != null) {
try {
file.close();
} catch (Exception e) {
}
}
// Close connection to server.
if (stream != null) {
try {
stream.close();
} catch (Exception e) {
}
}
}
}
private void stateChanged() {
setChanged();
notifyObservers();
}
}
(If you need more code, just let me know and I'll upload everything somewhere.)

How to pre-buffer an MP3 file completely in Java

I'm working on a music player, which receives a playlist with remote mp3 files (HTTP) and play them subsequently.
I want to have it start streaming the first track, if enough of the song is buffered to play it through, it should already begin to buffer the following song into memory. That is to make up for the unstable internet connection the program is supposed to run on.
How do I tell the BufferedInputStream to just download the whole file?
I'm happy to hear other suggestions on how to solve this, too.
I'm using the JLayer/BasicPlayer library to play audio, this is the code.
String mp3Url = "http://ia600402.us.archive.org/6/items/Stockfinster.-DeadLinesutemos025/01_Push_Push.mp3";
URL url = new URL(mp3Url);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
BasicPlayer player = new BasicPlayer();
player.open(bis);
player.play();
Here you will get an example for how to prebuffer audio file in java
Ok, to answer my question, here's a working implementation:
/**
* <code>DownloaderInputStream</code>
*/
public class DownloaderInputStream extends InputStream {
/**
* <code>IDownloadNotifier</code> - download listener.
*/
public static interface IDownloadListener {
/**
* Notifies about download completion.
*
* #param buf
* #param offset
* #param length
*/
public void onComplete(final byte[] buf, final int offset, final int length);
}
/**
* <code>ByteArrayOutputStreamX</code> - {#link ByteArrayOutputStream}
* extension that exposes buf variable (to avoid copying).
*/
private final class ByteArrayOutputStreamX extends ByteArrayOutputStream {
/**
* Constructor.
*
* #param size
*/
public ByteArrayOutputStreamX(final int size) {
super(size);
}
/**
* Returns inner buffer.
*
* #return inner buffer
*/
public byte[] getBuffer() {
return buf;
}
}
private final class Downloader extends Object implements Runnable {
// fields
private final InputStream is;
/**
* Constructor.
*
* #param is
*/
public Downloader(final InputStream is) {
this.is = is;
}
// Runnable implementation
public void run() {
int read = 0;
byte[] buf = new byte[16 * 1024];
try {
while ((read = is.read(buf)) != -1) {
if (read > 0) {
content.write(buf, 0, read);
downloadedBytes += read;
} else {
Thread.sleep(50);
}
}
} catch (Exception e) {
e.printStackTrace();
}
listener.onComplete(content.getBuffer(), 0 /*
* offset
*/, downloadedBytes);
}
}
// fields
private final int contentLength;
private final IDownloadListener listener;
// state
private ByteArrayOutputStreamX content;
private volatile int downloadedBytes;
private volatile int readBytes;
/**
* Constructor.
*
* #param contentLength
* #param is
* #param listener
*/
public DownloaderInputStream(final int contentLength, final InputStream is, final IDownloadListener listener) {
this.contentLength = contentLength;
this.listener = listener;
this.content = new ByteArrayOutputStreamX(contentLength);
this.downloadedBytes = 0;
this.readBytes = 0;
new Thread(new Downloader(is)).start();
}
/**
* Returns number of downloaded bytes.
*
* #return number of downloaded bytes
*/
public int getDownloadedBytes() {
return downloadedBytes;
}
/**
* Returns number of read bytes.
*
* #return number of read bytes
*/
public int getReadBytes() {
return readBytes;
}
// InputStream implementation
#Override
public int available() throws IOException {
return downloadedBytes - readBytes;
}
#Override
public int read() throws IOException {
// not implemented (not necessary for BasicPlayer)
return 0;
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
if (readBytes == contentLength) {
return -1;
}
int tr = 0;
while ((tr = Math.min(downloadedBytes - readBytes, len)) == 0) {
try {
Thread.sleep(100);
} catch (Exception e) {/*
* ignore
*/
}
}
byte[] buf = content.getBuffer();
System.arraycopy(buf, readBytes, b, off, tr);
readBytes += tr;
return tr;
}
#Override
public long skip(long n) throws IOException {
// not implemented (not necessary for BasicPlayer)
return n;
}
}

Android LocalServerSocket

In android, there are two classes LocalServerSocket and LocalSocket. I think they are something like AF_LOCAL in unix socket (I am not sure it is correct or not).
My question is that :
Is it possible to create LocalServerSocket in Java and use a normal unix socket client to connect to it in native or other process ?
If it is possible, what the "sockaddr_un.sun_path" I should set in native ?
I have written a sample project to test it, and I try to set the .sun_path as same as string name used in LocalServerSocket, but it failed, the native could not connect to the Java LocalServerSocket.
My Java code :
package test.socket;
import java.io.IOException;
import java.io.InputStream;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class TestSocketActivity extends Activity {
public static String SOCKET_ADDRESS = "my.local.socket.address";
public String TAG = "Socket_Test";
static{System.loadLibrary("testSocket");}
private native void clientSocketThreadNative();
private native void setStopThreadNative();
localServerSocket mLocalServerSocket;
localClientSocket mLocalClientSocket;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLocalServerSocket = new localServerSocket();
mLocalClientSocket = new localClientSocket();
}
/* LocalServerSocket */
public class localServerSocket extends Thread {
int bufferSize = 32;
byte[] buffer;
int bytesRead;
int totalBytesRead;
int posOffset;
LocalServerSocket server;
LocalSocket receiver;
InputStream input;
private volatile boolean stopThread;
public localServerSocket() {
Log.d(TAG, " +++ Begin of localServerSocket() +++ ");
buffer = new byte[bufferSize];
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
try {
server = new LocalServerSocket(SOCKET_ADDRESS);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "The LocalServerSocket created failed !!!");
e.printStackTrace();
}
stopThread = false;
}
public void run() {
Log.d(TAG, " +++ Begin of run() +++ ");
while (!stopThread) {
if (null == server){
Log.d(TAG, "The LocalServerSocket is NULL !!!");
stopThread = true;
break;
}
try {
Log.d(TAG, "LocalServerSocket begins to accept()");
receiver = server.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "LocalServerSocket accept() failed !!!");
e.printStackTrace();
continue;
}
try {
input = receiver.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "getInputStream() failed !!!");
e.printStackTrace();
continue;
}
Log.d(TAG, "The client connect to LocalServerSocket");
while (receiver != null) {
try {
bytesRead = input.read(buffer, posOffset,
(bufferSize - totalBytesRead));
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "There is an exception when reading socket");
e.printStackTrace();
break;
}
if (bytesRead >= 0) {
Log.d(TAG, "Receive data from socket, bytesRead = "
+ bytesRead);
posOffset += bytesRead;
totalBytesRead += bytesRead;
}
if (totalBytesRead == bufferSize) {
Log.d(TAG, "The buffer is full !!!");
String str = new String(buffer);
Log.d(TAG, "The context of buffer is : " + str);
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
}
}
Log.d(TAG, "The client socket is NULL !!!");
}
Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
if (receiver != null){
try {
receiver.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (server != null){
try {
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void setStopThread(boolean value){
stopThread = value;
Thread.currentThread().interrupt(); // TODO : Check
}
}
/* Client native socket */
public class localClientSocket extends Thread {
private volatile boolean stopThread;
public localClientSocket(){
Log.d(TAG, " +++ Begin of localClientSocket() +++ ");
stopThread = false;
}
public void run(){
Log.d(TAG, " +++ Begin of run() +++ ");
while(!stopThread){
clientSocketThreadNative();
}
}
public void setStopThread(boolean value){
stopThread = value;
setStopThreadNative();
Thread.currentThread().interrupt(); // TODO : Check
}
}
public void bt_startServerOnClick(View v) {
mLocalServerSocket.start();
}
public void bt_startClientOnClick(View v) {
mLocalClientSocket.start();
}
public void bt_stopOnClick(View v) {
mLocalClientSocket.setStopThread(true);
mLocalServerSocket.setStopThread(true);
}
}
My Native code :
#define SOCKET_NAME "my.local.socket.address"
JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_clientSocketThreadNative
(JNIEnv *env, jobject object){
LOGD("In clientSocketThreadNative() : Begin");
stopThread = 1;
int sk, result;
int count = 1;
int err;
char *buffer = malloc(8);
int i;
for(i = 0; i<8; i++){
buffer[i] = (i+1);
}
/*
struct sockaddr_un addr;
bzero((char *)&addr,sizeof(addr);
addr.sun_family = AF_UNIX;
addr.sun_path = SOCKET_NAME;
*/
struct sockaddr_un addr = {
AF_UNIX, SOCKET_NAME
};
LOGD("In clientSocketThreadNative() : Before creating socket");
sk = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sk < 0) {
err = errno;
LOGD("%s: Cannot open socket: %s (%d)\n",
__FUNCTION__, strerror(err), err);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Before connecting to Java LocalSocketServer");
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
err = errno;
LOGD("%s: connect() failed: %s (%d)\n",
__FUNCTION__, strerror(err), err);
close(sk);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Connecting to Java LocalSocketServer succeed");
while(!stopThread){
result = write(sk, buffer, 8);
LOGD("In clientSocketThreadNative() : Total write = %d", result);
count++;
if(4 == count){
sleep(1);
count = 0;
}
}
LOGD("In clientSocketThreadNative() : End");
}
Any suggestion would be greatly appreciated !!!
The following code might not be perfect but it works !!! Thanks for Mike.
Java part (Socket Server) :
package test.socket;
import java.io.IOException;
import java.io.InputStream;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class TestSocketActivity extends Activity {
public static String SOCKET_ADDRESS = "/test/socket/localServer";
public String TAG = "Socket_Test";
static{System.loadLibrary("testSocket");}
private native void clientSocketThreadNative();
private native void setStopThreadNative();
localSocketServer mLocalSocketServer;
localSocketClient mLocalSocketClient;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLocalSocketServer = new localSocketServer();
mLocalSocketClient = new localSocketClient();
}
/* LocalSocketServer */
public class localSocketServer extends Thread {
int bufferSize = 32;
byte[] buffer;
int bytesRead;
int totalBytesRead;
int posOffset;
LocalServerSocket server;
LocalSocket receiver;
InputStream input;
private volatile boolean stopThread;
public localSocketServer() {
Log.d(TAG, " +++ Begin of localSocketServer() +++ ");
buffer = new byte[bufferSize];
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
try {
server = new LocalServerSocket(SOCKET_ADDRESS);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "The localSocketServer created failed !!!");
e.printStackTrace();
}
LocalSocketAddress localSocketAddress;
localSocketAddress = server.getLocalSocketAddress();
String str = localSocketAddress.getName();
Log.d(TAG, "The LocalSocketAddress = " + str);
stopThread = false;
}
public void run() {
Log.d(TAG, " +++ Begin of run() +++ ");
while (!stopThread) {
if (null == server){
Log.d(TAG, "The localSocketServer is NULL !!!");
stopThread = true;
break;
}
try {
Log.d(TAG, "localSocketServer begins to accept()");
receiver = server.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "localSocketServer accept() failed !!!");
e.printStackTrace();
continue;
}
try {
input = receiver.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "getInputStream() failed !!!");
e.printStackTrace();
continue;
}
Log.d(TAG, "The client connect to LocalServerSocket");
while (receiver != null) {
try {
bytesRead = input.read(buffer, posOffset,
(bufferSize - totalBytesRead));
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "There is an exception when reading socket");
e.printStackTrace();
break;
}
if (bytesRead >= 0) {
Log.d(TAG, "Receive data from socket, bytesRead = "
+ bytesRead);
posOffset += bytesRead;
totalBytesRead += bytesRead;
}
if (totalBytesRead == bufferSize) {
Log.d(TAG, "The buffer is full !!!");
String str = new String(buffer);
Log.d(TAG, "The context of buffer is : " + str);
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
}
}
Log.d(TAG, "The client socket is NULL !!!");
}
Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
if (receiver != null){
try {
receiver.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (server != null){
try {
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void setStopThread(boolean value){
stopThread = value;
Thread.currentThread().interrupt(); // TODO : Check
}
}
/* Client native socket */
public class localSocketClient extends Thread {
private volatile boolean stopThread;
public localSocketClient(){
Log.d(TAG, " +++ Begin of localSocketClient() +++ ");
stopThread = false;
}
public void run(){
Log.d(TAG, " +++ Begin of run() +++ ");
while(!stopThread){
clientSocketThreadNative();
}
}
public void setStopThread(boolean value){
stopThread = value;
setStopThreadNative();
Thread.currentThread().interrupt(); // TODO : Check
}
}
public void bt_startServerOnClick(View v) {
mLocalSocketServer.start();
}
public void bt_startClientOnClick(View v) {
mLocalSocketClient.start();
}
public void bt_stopOnClick(View v) {
mLocalSocketClient.setStopThread(true);
mLocalSocketServer.setStopThread(true);
}
}
Native C part (Client)
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/un.h>
#include "test_socket_TestSocketActivity.h"
#define LOCAL_SOCKET_SERVER_NAME "/test/socket/localServer"
volatile int stopThread;
#ifndef __JNILOGGER_H_
#define __JNILOGGER_H_
#include <android/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LOG_TAG
#define LOG_TAG "NativeSocket"
#endif
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__)
#define LOGS(...) __android_log_print(ANDROID_LOG_SILENT,LOG_TAG,__VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif /* __JNILOGGER_H_ */
JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_clientSocketThreadNative
(JNIEnv *env, jobject object){
LOGD("In clientSocketThreadNative() : Begin");
stopThread = 0;
int sk, result;
int count = 1;
int err;
char *buffer = malloc(8);
int i;
for(i = 0; i<8; i++){
buffer[i] = (i+1);
}
struct sockaddr_un addr;
socklen_t len;
addr.sun_family = AF_LOCAL;
/* use abstract namespace for socket path */
addr.sun_path[0] = '\0';
strcpy(&addr.sun_path[1], LOCAL_SOCKET_SERVER_NAME );
len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&addr.sun_path[1]);
LOGD("In clientSocketThreadNative() : Before creating socket");
sk = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sk < 0) {
err = errno;
LOGD("%s: Cannot open socket: %s (%d)\n",
__FUNCTION__, strerror(err), err);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Before connecting to Java LocalSocketServer");
if (connect(sk, (struct sockaddr *) &addr, len) < 0) {
err = errno;
LOGD("%s: connect() failed: %s (%d)\n",
__FUNCTION__, strerror(err), err);
close(sk);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Connecting to Java LocalSocketServer succeed");
while(!stopThread){
result = write(sk, buffer, 8);
LOGD("In clientSocketThreadNative() : Total write = %d", result);
count++;
if(4 == count){
sleep(1);
count = 0;
}
}
LOGD("In clientSocketThreadNative() : End");
}
JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_setStopThreadNative
(JNIEnv *env, jobject object){
stopThread = 1;
}
Looking at local_socket_client.c in the Android source, it looks like they do this:
int socket_make_sockaddr_un(const char *name, int namespaceId,
struct sockaddr_un *p_addr, socklen_t *alen)
{
memset (p_addr, 0, sizeof (*p_addr));
size_t namelen;
switch (namespaceId) {
case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
namelen = strlen(name);
// Test with length +1 for the *initial* '\0'.
if ((namelen + 1) > sizeof(p_addr->sun_path)) {
goto error;
}
/*
* Note: The path in this case is *not* supposed to be
* '\0'-terminated. ("man 7 unix" for the gory details.)
*/
p_addr->sun_path[0] = 0;
memcpy(p_addr->sun_path + 1, name, namelen);
...
p_addr->sun_family = AF_LOCAL;
*alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
It seems like the memset() is important because the entire sun_path is relevant. (looks like you cover that part with your structure initialization, though.) And it's not "0" plus the original name, it's an actual zero byte! (its value is all binary zeroes, not an ascii '0')
Try following more closely what they're doing, including the leading '\0' byte and the AF_LOCAL family.
If you have updated code (whether it works or not) please post it! I'm interested in your results. Did you ever get this to work?
If it doesn't work, find out what errno is and either call perror() to print it to stderr, or call strerror() and log the output. Let us know what error you get.
Edit
I recently solved this problem in a project of my own. I found that the key was to specify the correct length when calling connect() and bind(). In the code I posted above, it calculates the length by using the offset of the sun_path in the structure, plus the length of the name, plus one for the leading '\0' byte. If you specify any other length, Java code might not be able to connect to the socket.
Confirmed that the above works properly. The following examples will not only work together, but will work with the corresponding Android LocalSocket and LocalServerSocket classes:
Client side (Android is server using LocalServerSocket):
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <sys/un.h> /* struct sockaddr_un */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
#include <errno.h>
#include <stddef.h>
#define RCVBUFSIZE 2048 /* Size of receive buffer */
void DieWithError(char *errorMessage) /* Error handling function */
{
fprintf(stderr, "Error: %s - %s\n", errorMessage, strerror(errno));
exit(errno);
}
int main(int argc, char *argv[])
{
int sock; /* Socket descriptor */
struct sockaddr_un echoServAddr; /* Echo server address */
unsigned char *localSocketName = "MyTestSocket";
static unsigned char echoString[] = {0x80, 0x00, 0x0e, 0x10, 0x00, 0x9c, 0x40, 0xc9, 0x20, 0x20, 0x20, 0x32, 0x00, 0x00};
static unsigned int echoStringLen = sizeof(echoString); /* Length of string to echo */
unsigned char echoBuffer[RCVBUFSIZE]; /* Buffer for echo string */
int bytesRcvd, totalBytesRcvd; /* Bytes read in single recv()
and total bytes read */
int size;
int i;
/* Create a reliable, stream socket using Local Sockets */
if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
DieWithError("socket() failed");
/* Construct the server address structure */
memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */
echoServAddr.sun_family = AF_LOCAL; /* Local socket address family */
/**
* Tricky and obscure! For a local socket to be in the "Android name space":
* - The name of the socket must have a 0-byte value as the first character
* - The linux man page is right in that 0 bytes are NOT treated as a null terminator.
* - The man page is not clear in its meaning when it states that "the rest of the bytes in
* sunpath" are used. "Rest of bytes" is determined by the length passed in for
* sockaddr_len and Android sets this per the recommended file-system sockets of
* sizeof(sa_family_t) + strlen(sun_path) + 1. This is important when making calls
* to bind, connect, etc!
* We have initialized the struct sockaddr_un to zero already, so all that is needed is to
* begin the name copy at sun_path[1] and restrict its length to sizeof(echoServAddr.sun_path)-2
**/
strncpy(echoServAddr.sun_path + 1, localSocketName, sizeof(echoServAddr.sun_path) - 2);
size = sizeof(echoServAddr) - sizeof(echoServAddr.sun_path) + strlen(echoServAddr.sun_path+1) + 1;
/* Establish the connection to the echo server */
if (connect(sock, (struct sockaddr *) &echoServAddr, size) < 0)
DieWithError("connect() failed");
/* Send the string to the server */
if (send(sock, echoString, echoStringLen, 0) != echoStringLen)
DieWithError("send() sent a different number of bytes than expected");
/* Receive the same string back from the server */
totalBytesRcvd = 0;
printf("Sent: ");
for (i = 0; i < echoStringLen; i++)
printf("%02X ", echoString[i]);
printf("\n"); /* Print a final linefeed */
printf("Received: "); /* Setup to print the echoed string */
if ((bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0)) <= 0)
DieWithError("recv() failed or connection closed prematurely");
for (i = 0; i < bytesRcvd; i++)
printf("%02X ", echoBuffer[i]);
printf("\n"); /* Print a final linefeed */
close(sock);
exit(0);
}
Server side (Android is client using LocalSocket):
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <sys/un.h> /* struct sockaddr_un */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
#include <errno.h>
#include <stddef.h>
#define RCVBUFSIZE 2048 /* Size of receive buffer */
void DieWithError(char *errorMessage) /* Error handling function */
{
fprintf(stderr, "Error: %s - %s\n", errorMessage, strerror(errno));
exit(errno);
}
void HandleLocalClient(int clntSocket)
{
char echoBuffer[RCVBUFSIZE]; /* Buffer for echo string */
int recvMsgSize; /* Size of received message */
/* Receive message from client */
if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
DieWithError("recv() failed");
/* Send received string and receive again until end of transmission */
while (recvMsgSize > 0) /* zero indicates end of transmission */
{
/* Echo message back to client */
if (send(clntSocket, echoBuffer, recvMsgSize, 0) != recvMsgSize)
DieWithError("send() failed");
/* See if there is more data to receive */
if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
DieWithError("recv() failed");
}
close(clntSocket); /* Close client socket */
}
#define MAXPENDING 5 /* Maximum outstanding connection requests */
void DieWithError(char *errorMessage); /* Error handling function */
void HandleLocalClient(int clntSocket); /* TCP client handling function */
int main(int argc, char *argv[])
{
int servSock; /* Socket descriptor for server */
int clntSock; /* Socket descriptor for client */
struct sockaddr_un echoClntAddr; /* Client address */
unsigned int clntLen; /* Length of client address data structure */
struct sockaddr_un echoServAddr; /* Echo server address */
unsigned char *localSocketName = "MyTestSocket";
static unsigned int echoStringLen = 14; /* Length of string to echo */
unsigned char echoBuffer[RCVBUFSIZE]; /* Buffer for echo string */
int bytesRcvd, totalBytesRcvd; /* Bytes read in single recv()
and total bytes read */
int size;
int i;
/* Create a reliable, stream socket using Local Sockets */
if ((servSock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
DieWithError("socket() failed");
/* Construct the server address structure */
memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */
echoServAddr.sun_family = AF_LOCAL; /* Local socket address family */
/**
* Tricky and obscure! For a local socket to be in the "Android name space":
* - The name of the socket must have a 0-byte value as the first character
* - The linux man page is right in that 0 bytes are NOT treated as a null terminator.
* - The man page is not clear in its meaning when it states that "the rest of the bytes in
* sunpath" are used. "Rest of bytes" is determined by the length passed in for
* sockaddr_len and Android sets this per the recommended file-system sockets of
* sizeof(sa_family_t) + strlen(sun_path) + 1. This is important when making calls
* to bind, connect, etc!
* We have initialized the struct sockaddr_un to zero already, so all that is needed is to
* begin the name copy at sun_path[1] and restrict its length to sizeof(echoServAddr.sun_path)-2
**/
strncpy(echoServAddr.sun_path + 1, localSocketName, sizeof(echoServAddr.sun_path) - 2);
size = sizeof(echoServAddr) - sizeof(echoServAddr.sun_path) + strlen(echoServAddr.sun_path+1) + 1;
/* Bind to the local address */
if (bind(servSock, (struct sockaddr *) &echoServAddr, size) < 0)
DieWithError("bind() failed");
/* Mark the socket so it will listen for incoming connections */
if (listen(servSock, MAXPENDING) < 0)
DieWithError("listen() failed");
for (;;) /* Run forever */
{
/* Set the size of the in-out parameter */
clntLen = sizeof(echoClntAddr);
/* Wait for a client to connect */
if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0)
DieWithError("accept() failed");
/* clntSock is connected to a client! */
printf("Handling client\n");
HandleLocalClient(clntSock);
}
/* NOT REACHED */
return 0;
}
Tested with Android 4.0.3 ICS 03/15
Thanks for the answers here. Piecing people's responses together, the things to be careful about are
On the native side:
the name of the socket must have a 0-byte value as the first character
The linux man page is right in that 0 bytes are NOT treated as a null terminator.
The man page is not clear in its meaning when it states that "the rest of the bytes in sunpath" are used. "Rest of bytes" is determined by the length passed in for sockaddr_len and Android sets this per the recommended file-system sockets of sizeof(sa_family_t) + strlen(sun_path) + 1. This is important when making calls to bind, connect, etc!
On the Java side:
Android defines the conventions it uses under the covers but are consistent with above.

Categories

Resources