I'm working on a Android app that acts as TCP server/client i want to send data from the app to a server/client that is written in c/c++(made with the boost libraray). I have a normal Java function that calls a native c function for string converstion to bytes:
The function is defined as followed (the native function is Convert String:
// Send buffer, the method can be used by both client and server objects.
public void SendBuffer(String Buffer){
try {
// Convert char to string to byte
byte[] Temp = new byte[10];
String Teststring = "AAAAAAAABB";
Temp = ConvertString(Teststring);
//byte[] Temp = new String(Buffer).getBytes();
// Get socket output stream
OutputStream OutputBuffer = ClientSocket.getOutputStream();
//Write byte data to outputstream
OutputBuffer.write(Temp);
// Neatly flush and close the outputbuffer
OutputBuffer.flush();
OutputBuffer.close();
}
catch (IOException e) {
Log.e("TCPIPCommunicator: ", "Client: Failed to send", e);
e.printStackTrace();
}
}
The function ConvertString is a native function that converts the Java string to a C/C++ string and returns it as Java bytes, it is defined as followed:
JNIEXPORT jbyteArray JNICALL Java_com_example_communicationmoduleTCPIP_communicationmoduleTCPIP_ConvertString(
JNIEnv * env, jobject,
jstring Buffer)
{
// Array to fill with data
jbyteArray Array;
// Init java byte array
Array = env->NewByteArray(10);
const char* NewBuffer = env->GetStringUTFChars(Buffer, 0);
// Set byte array region with the size of the SendData CommStruct.
// Now we can send the data back.
env->SetByteArrayRegion(Array, 0, 10, (jbyte*)NewBuffer);
env->ReleaseStringUTFChars(Buffer, NewBuffer);
// Return java array
return Array;
}
}
When i run the program i get two 'AAAA' on the c side but not as a whole array ( so no 'AAAAAAAADD). I think the problem is that the server sends 2 'AAAA' and not the whole array at once. The client crashes with the following error:
'boost::exception_detail::clone_impl >'
what(): read: End of file
Does the java server sends the data wrong? can anyone give me a suggestion? all feedback is welcome!
Are you willing to send the data or to make the JNI stuff work?
In the former case, use Java to convert a string into UTF-8 (which will be ASCII for English.)
Conversion of text byte[] -> byte[] is not exactly what you need, but you'll get the idea:
//byte[] result;
//byte[] source;
String s = new String(source,"UTF-8");
result = s.getBytes("UTF-16LE");
For the 2nd case, I can share a portion of working code; it calls Java to convert from one encoding to another
// it returns NULL in the case of an exception
// the returned memory is calloc()'d; it's the caller's responsibility to free() it.
char* changeEncoding(const char*source, int len, int direction)
{
JNIEnv* env = threadUnsafeInfo.env;
jobject obj = threadUnsafeInfo.obj;
if (!source) {
JNU_ThrowByName(env, "java/lang/NullPointerException", 0);
return NULL;
}
jbyteArray srcArray = env->NewByteArray(len);
jclass cls = env->FindClass("com/xyz/MyClass");
jmethodID mid = env->GetMethodID(cls, "convert", "([BI)[B");
if (mid != NULL && srcArray != NULL) {
env->SetByteArrayRegion(srcArray, 0, len, (jbyte*)source);
env->ExceptionClear();
//jbyteArray resArray = (jbyteArray)env->CallStaticObjectMethod(cls, mid, srcArray, direction);
jbyteArray resArray = (jbyteArray)env->CallObjectMethod(obj, mid, srcArray, direction);
if(env->ExceptionOccurred()) {
DLOG("exception in convert ([BI)[B");
env->ExceptionDescribe();
//env->ExceptionClear(); // ??
return NULL;
}
int resultLen = env->GetArrayLength(resArray);
char* result = (char*)calloc(2 + resultLen,1); // why 2: a bit of healthy paranoia ain't gonna hurt anyone
if (result == 0) {
JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0);
return NULL;
}
env->GetByteArrayRegion(resArray, 0, resultLen, (jbyte *)result);
env->DeleteLocalRef(cls);
env->DeleteLocalRef(resArray);
env->DeleteLocalRef(srcArray);
return result;
} else {
JNU_ThrowByName(env, "java/lang/NullPointerException", 0);
myassert(("method id = 0",0));
}
return NULL;
}
In the code that I have at hand
I did not use jstrings, preferring the byte arrays.
Related
i am getting java.lang.Byte[] from c(jbyteArray in JNI) . How to convert this jbyteArray to string in java.
c function is
jbyteArray Java_eu_ratikal_helloc_MainActivity_getNameBytes(JNIEnv* env, jobject thiz) {
int n=0;
jstring na ="HelloAndroidString";
char* p = "HelloAndroidString";
while(*p++){
n++;
}
jbyteArray arr = (*env)->NewByteArray(env, n);
(*env)->SetByteArrayRegion(env,arr,0,n, (jbyte*)na);
char* b = (char*)arr;
return arr;
}
java code is
byte[] bytes = getNameBytes();
Getting compilation error like
Incompatible types
Required byte[]
Found java.lang.Byte[]
You need to return
public native byte[] getNamBytes();
but to convert it into a String you need to use new String e.g.
public String String getName() {
return new String(getNameBytes(), "UTF-8");
}
I am new to JNI and have a requirement to convert a char[] to jcharArray and then create a String in JAVA. This String will be passed in a Parcel to other layers. When reading from the Parcel I want the other component to get the same byte sequence as of char array.
jchar *jcharBuffer1 = (jchar *)calloc(sizeof(jchar), length+1);
for (count = 0; count<length; count ++) {
jcharBuffer1[count] = (jchar)c_array[count];
}
jcharArray str1 = env->NewCharArray(length+1);
env->SetCharArrayRegion (str1, 0, length+1, jcharBuffer1);
This is my java code
public void testing(char[] array){
String test_str = new String(array);
Setdata(test_str)
}
public void Setdata(String data) {
Parcel request = Parcel.obtain();
Parcel resp = Parcel.obtain();
try {
.
.
request.writeString(data);
}
}
In the whole process I want the same byte sequence to arrive even after converting in to string. These bytes are decoded output from Base64 string from a server.
1. The jchar is 16bits but C-byte is 8 bit. So how can I achieve this requirement?
How Can I Use the ASN1 decoder (libtasn1-3.3) to print the content of a DD certificate pem received as parameter?
asn1Decoding is a program from libtasn1-3.3
If that program works for you, then the ansn1Decoding has a decode function which you can use (of course you have to adapt this code out. If you don't want to modify by moving the decode code out to your program, then you have to replicate the decode code into your code):
static int
decode (asn1_node definitions, const char *typeName, void *der, int der_len,
int benchmark)
If you want to call the decoding from your program, then you have to
asn1_node definitions = NULL;
int asn1_result = ASN1_SUCCESS;
unsigned char *der;
int der_len = 0, benchmark = 0;
// Please test with asn1Decoding application manually to find the correct typeName for your code
char typeName[] = {"PKIX1.Certificate"};
asn1_result = asn1_parser2tree ("Your File Name", &definitions, errorDescription);
if (asn1_result != ASN1_SUCCESS) { exit(1); }
{
size_t tmplen;
der = (unsigned char *) read_binary_file (inputFileDerName, &tmplen);
der_len = tmplen;
}
if (der == NULL)
{
asn1_delete_structure (&definitions);
exit (1);
}
if (decode (definitions, typeName, der, der_len, benchmark) != ASN1_SUCCESS)
{
asn1_delete_structure (&definitions);
}
I am trying to pass char* from C++ to java using JNI in android.
I have tried number of ways to pass that data
1) Using NewStringUTF:
const char* data = getData(); // this method returns a char array.
env->NewStringUTF(data);
Executing above code throws below error
JNI WARNING: input is not valid Modified UTF-8: illegal continuation byte 0x70.
2) Using NewString:
const char* data = getData(); // this method returns a char array.
// passing a byte array to java
jbyteArray trackIDArray = env->NewByteArray(strlen(data));
env->SetByteArrayRegion(trackIDArray, 0, strlen(data), (const jbyte*)trackID);
On java side, I am getting some garbage value. I don't understand how to get this char array to Java.
1) your data is simply not a valid UTF-8 string. Not every char array is automatically a valid UTF-8. You probably have it in some single-byte encoding (like ISO or Windows CP), or it's not a readable string at all.
2) should be ok, but show the code which fills trackID from data. The fact that you need to hard typecast it to jbyte* is suspicious. This code might be correct, but you can make a mistake on Java side too:
If data is not a readable string or is in single-byte encoding which is not "platform's default charset" java.lang.String(byte[]) constructor won't be able to make a readable string out of it! In that case, you must convert to UTF-8 on C side. You will also release yourself from the dependency on platform specific encoding (which may be wildly different).
I would suspect data instead of trackID.
env->SetByteArrayRegion(trackIDArray, 0, strlen(data), (const jbyte*)data);
Then you have the bytes and on the java side may look what encoding it is - by a hex dump or other inspection.
Later:
String s = new String(data, "Cp1252"); // Or so.
NewStringUTF expects you to pass a Modified UTF-8 string.
You are likely trying to pass UTF-8.
There are multiple ways to fix it:
Most obvious one is to encode the string to UTF-8 modified in C++ before passing it to Java.
Another way is to pass it to Java as a byte array and use String constructor to convert it from UTF-16.
The second way might be more efficient as in the end Java uses UTF-16 for string representation.
As an alternative approach, you could convert the string to UTF-16 in C++ and pass it to newString JNI function which expects UTF-16.
I put very big bytesources (>2kbyte) behind the JNI like this :
Content of a csv-table:
R"xxx(tbl_Cbla,Column 02,Column 03,Column 04
sdfsdsad,sdfasd,dsfaddf,fdsasdf,fafasa
18,1,10,8,0)xxx"`
std::string data1 =
#include "big_table1.csv"
;
std::string data2 =
#include "big_table2.csv"
;
extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_example_bigData_rawResource(
JNIEnv *env,
jobject /* this */, jint index) {
std::string values;
switch (index) {
case 0: {values = data1;break;}
case 1: {values = data2;break;}
}
int byteCount = values.length();
jbyteArray ret = env->NewByteArray(byteCount);
const jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(values.c_str());
env->SetByteArrayRegion (ret, 0, byteCount, pNativeMessage);
return ret;
}
In Java you can get it back like this, to import the native function is up to you:
ByteArrayInputStream bis = null;
try {
bis = new ByteArrayInputStream(rawResource(1);
} catch (Exception e) {
e.printStackTrace();
}
BufferedReader buffer = new BufferedReader(new InputStreamReader(bis, Charset.forName("UTF-8")));
To to handle the buffered reader is also up to you, small exg.:
Strig line = buffer.readLine();
while ((line = buffer.readLine()) != null) {
//play around with 'line'
}
I have a Java method that process a bitmap and returns a String.
When I call this method from JNI (VS 2010) it works, but if I call this method many times,
the memory of the process grown up until crash.
The instruction that use a lot of memory is:
jbyteArray jBuff = _env->NewByteArray(b->Length);
My code:
static jobject staticArray=0;
System::String^ MyClass::ExecuteJavaMethod(System::Drawing::Bitmap^ bmp)
{
JNIEnv *_env;
System::String^ out;
unsigned const char * buff;
int res = jvm->AttachCurrentThread((void **)&_env, NULL);
if (jvm->GetEnv((void**) &_env, JNI_VERSION_1_6) != JNI_OK)
{
return "GetEnv ERROR";
}
//save the bitmap in the stream
MemoryStream^ ms = gcnew MemoryStream();
bmp->Save(ms, ImageFormat::Bmp);
//get the bitmap buffer
array<unsigned char>^b = ms->GetBuffer() ;
//unmanaged conversion
buff = GetUnmanaged(b,b->Length);
//fill the buffer
jbyteArray jBuff = _env->NewByteArray(b->Length);
_env->SetByteArrayRegion(jBuff, 0, b->Length, (jbyte*) buff);
//call the java method
jstring str = (jstring) _env->CallStaticObjectMethod ( Main,
javaMethod,
jBuff);
// _env->ReleaseByteArrayElements(jBuff,(jbyte*)buff), 0); //NOT WORKING
//staticArray= _env->NewGlobalRef(jBuff); NOT
//_env->DeleteLocalRef(jBuff); WORKING
//return the string result of the java method
return gcnew String(env->GetStringUTFChars(str, 0));
}
the answer is: _env->DeleteLocalRef(jBuff);
You didn't call DetachCurrentThread() for each AttachCurrentThread(), which is requested in Java Native Interface Specification. That makes the local references (jBuff and str) unable to be freed automatically. Also, the const char* fetched through GetStringUTFChars() needs to be released.
The correct way is change
return gcnew String(env->GetStringUTFChars(str, 0));
into
const char* cstr = env->GetStringUTFChars(str, 0);
System::String^ retstr = gcnew String(cstr);
env->ReleaseStringUTFChars(str, cstr);
jvm->DetachCurrentThread();
return retstr;