mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-04 23:02:02 +01:00
28318eb38a
Couldn't figure out how to do it at compile-time on Android.
1054 lines
31 KiB
C
1054 lines
31 KiB
C
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
|
|
/*
|
|
* Copyright © 2009 - 2023 by Eric House (xwords@eehouse.org). All rights
|
|
* reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
#include "andutils.h"
|
|
#include "paths.h"
|
|
|
|
#include "comtypes.h"
|
|
#include "xwstream.h"
|
|
#include "strutils.h"
|
|
#include "dbgutil.h"
|
|
|
|
void
|
|
and_assert( const char* test, int line, const char* file, const char* func )
|
|
{
|
|
RAW_LOG( "assertion \"%s\" failed: line %d in %s() in %s",
|
|
test, line, func, file );
|
|
XP_LOGF( "assertion \"%s\" failed: line %d in %s() in %s",
|
|
test, line, func, file );
|
|
|
|
/* give log a chance to write before __android_log_assert() kills the
|
|
process */
|
|
struct timespec req = {
|
|
.tv_nsec = 400000000, /* 4/10 second */
|
|
};
|
|
nanosleep( &req, NULL );
|
|
|
|
__android_log_assert( test, "ASSERT", "line %d in %s() in %s",
|
|
line, func, file );
|
|
}
|
|
|
|
#ifdef __LITTLE_ENDIAN
|
|
XP_U32
|
|
and_ntohl(XP_U32 ll)
|
|
{
|
|
XP_U32 result = 0L;
|
|
for ( int ii = 0; ii < 4; ++ii ) {
|
|
result <<= 8;
|
|
result |= ll & 0x000000FF;
|
|
ll >>= 8;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
XP_U16
|
|
and_ntohs( XP_U16 ss )
|
|
{
|
|
XP_U16 result;
|
|
result = ss << 8;
|
|
result |= ss >> 8;
|
|
return result;
|
|
}
|
|
|
|
XP_U32
|
|
and_htonl( XP_U32 ll )
|
|
{
|
|
return and_ntohl( ll );
|
|
}
|
|
|
|
|
|
XP_U16
|
|
and_htons( XP_U16 ss )
|
|
{
|
|
return and_ntohs( ss );
|
|
}
|
|
#else
|
|
error error error
|
|
#endif
|
|
|
|
jfieldID
|
|
getFieldID( JNIEnv* env, jobject obj, const char* fieldName, const char* fieldSig )
|
|
{
|
|
jclass cls = (*env)->GetObjectClass( env, obj );
|
|
XP_ASSERT( !!cls );
|
|
jfieldID fid = (*env)->GetFieldID( env, cls, fieldName, fieldSig );
|
|
XP_ASSERT( !!fid );
|
|
deleteLocalRef( env, cls );
|
|
return fid;
|
|
}
|
|
|
|
int
|
|
getInt( JNIEnv* env, jobject obj, const char* name )
|
|
{
|
|
jfieldID fid = getFieldID( env, obj, name, "I");
|
|
XP_ASSERT( !!fid );
|
|
int result = (*env)->GetIntField( env, obj, fid );
|
|
return result;
|
|
}
|
|
|
|
void
|
|
getInts( JNIEnv* env, void* cobj, jobject jobj, const SetInfo* sis, XP_U16 nSis )
|
|
{
|
|
for ( int ii = 0; ii < nSis; ++ii ) {
|
|
const SetInfo* si = &sis[ii];
|
|
uint8_t* ptr = ((uint8_t*)cobj) + si->offset;
|
|
int val = getInt( env, jobj, si->name );
|
|
switch( si->siz ) {
|
|
case 4:
|
|
*(uint32_t*)ptr = val;
|
|
break;
|
|
case 2:
|
|
*(uint16_t*)ptr = val;
|
|
break;
|
|
case 1:
|
|
*ptr = val;
|
|
break;
|
|
}
|
|
/* XP_LOGF( "%s: wrote int %s of size %d with val %d at offset %d", */
|
|
/* __func__, si->name, si->siz, val, si->offset ); */
|
|
}
|
|
}
|
|
|
|
void
|
|
setInt( JNIEnv* env, jobject obj, const char* name, int value )
|
|
{
|
|
jfieldID fid = getFieldID( env, obj, name, "I");
|
|
XP_ASSERT( !!fid );
|
|
(*env)->SetIntField( env, obj, fid, value );
|
|
}
|
|
|
|
void
|
|
setInts( JNIEnv* env, jobject jobj, void* cobj, const SetInfo* sis, XP_U16 nSis )
|
|
{
|
|
for ( int ii = 0; ii < nSis; ++ii ) {
|
|
const SetInfo* si = &sis[ii];
|
|
uint8_t* ptr = ((uint8_t*)cobj) + si->offset;
|
|
int val;
|
|
switch( si->siz ) {
|
|
case 4:
|
|
val = *(uint32_t*)ptr;
|
|
break;
|
|
case 2:
|
|
val = *(uint16_t*)ptr;
|
|
break;
|
|
case 1:
|
|
val = *ptr;
|
|
break;
|
|
default:
|
|
val = 0;
|
|
XP_ASSERT(0);
|
|
}
|
|
setInt( env, jobj, si->name, val );
|
|
/* XP_LOGFF( "read int %s of size %d with val %d/0x%x from offset %d", */
|
|
/* si->name, si->siz, val, val, si->offset ); */
|
|
}
|
|
}
|
|
|
|
bool
|
|
setBool( JNIEnv* env, jobject obj, const char* name, bool value )
|
|
{
|
|
bool success = false;
|
|
jfieldID fid = getFieldID( env, obj, name, "Z" );
|
|
if ( 0 != fid ) {
|
|
(*env)->SetBooleanField( env, obj, fid, value );
|
|
success = true;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void
|
|
setBools( JNIEnv* env, jobject jobj, void* cobj, const SetInfo* sis, XP_U16 nSis )
|
|
{
|
|
for ( int ii = 0; ii < nSis; ++ii ) {
|
|
const SetInfo* si = &sis[ii];
|
|
XP_Bool val = *(XP_Bool*)(((uint8_t*)cobj)+si->offset);
|
|
setBool( env, jobj, si->name, val );
|
|
/* XP_LOGF( "%s: read bool %s with val %d from offset %d", __func__, */
|
|
/* si->name, val, si->offset ); */
|
|
}
|
|
}
|
|
|
|
bool
|
|
setString( JNIEnv* env, jobject container, const char* fieldName, const XP_UCHAR* value )
|
|
{
|
|
// XP_LOGFF( "(fieldName=%s, val=%s)", fieldName, value );
|
|
bool success = false;
|
|
/* jfieldID fid = getFieldID( env, obj, name, "Ljava/lang/String;" ); */
|
|
|
|
jstring str = (*env)->NewStringUTF( env, value );
|
|
setObjectField( env, container, fieldName, "Ljava/lang/String;", str );
|
|
success = true;
|
|
|
|
#ifdef DEBUG
|
|
XP_UCHAR buf[1024];
|
|
getString( env, container, fieldName, buf, VSIZE(buf) );
|
|
XP_ASSERT( !value || 0 == XP_STRCMP( buf, value ) );
|
|
#endif
|
|
|
|
// XP_LOGFF( "(%s, %s) => %s", fieldName, value, boolToStr(success) );
|
|
return success;
|
|
}
|
|
|
|
void
|
|
getStrings( JNIEnv* env, void* cobj, jobject jobj, const SetInfo* sis, XP_U16 nSis )
|
|
{
|
|
for ( int ii = 0; ii < nSis; ++ii ) {
|
|
const SetInfo* si = &sis[ii];
|
|
XP_UCHAR* buf = (XP_UCHAR*)(((uint8_t*)cobj) + si->offset);
|
|
getString( env, jobj, si->name, buf, si->siz );
|
|
}
|
|
}
|
|
|
|
void
|
|
setStrings( JNIEnv* env, jobject jobj, void* cobj, const SetInfo* sis, XP_U16 nSis )
|
|
{
|
|
for ( int ii = 0; ii < nSis; ++ii ) {
|
|
const SetInfo* si = &sis[ii];
|
|
// XP_LOGF( "calling setString(%s)", si->name );
|
|
XP_UCHAR* val = (XP_UCHAR*)(((uint8_t*)cobj) + si->offset);
|
|
setString( env, jobj, si->name, val );
|
|
}
|
|
}
|
|
|
|
void
|
|
getString( JNIEnv* env, jobject container, const char* name, XP_UCHAR* buf,
|
|
int bufLen )
|
|
{
|
|
jstring jstr = getObjectField( env, container, name, "Ljava/lang/String;" );
|
|
jsize len = 0;
|
|
if ( !!jstr ) { /* might be null */
|
|
len = (*env)->GetStringUTFLength( env, jstr );
|
|
XP_ASSERT( len < bufLen );
|
|
const char* chars = (*env)->GetStringUTFChars( env, jstr, NULL );
|
|
XP_MEMCPY( buf, chars, len );
|
|
(*env)->ReleaseStringUTFChars( env, jstr, chars );
|
|
deleteLocalRef( env, jstr );
|
|
}
|
|
buf[len] = '\0';
|
|
// XP_LOGFF( "(field: %s) => '%s'", name, buf );
|
|
}
|
|
|
|
XP_UCHAR*
|
|
getStringCopy( MPFORMAL JNIEnv* env, jstring jstr )
|
|
{
|
|
XP_UCHAR* result = NULL;
|
|
if ( NULL != jstr ) {
|
|
const char* chars = (*env)->GetStringUTFChars( env, jstr, NULL );
|
|
result = copyString( mpool, chars );
|
|
(*env)->ReleaseStringUTFChars( env, jstr, chars );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static jobject
|
|
getObjectFieldWithFID( JNIEnv* env, jobject obj, const char* fieldName,
|
|
const char* sig, jfieldID* fidp )
|
|
{
|
|
jfieldID fid = getFieldID( env, obj, fieldName, sig );
|
|
XP_ASSERT( !!fid );
|
|
if ( !!fidp ) {
|
|
*fidp = fid;
|
|
}
|
|
jobject result = (*env)->GetObjectField( env, obj, fid );
|
|
return result;
|
|
}
|
|
|
|
jobject
|
|
getObjectField( JNIEnv* env, jobject container, const char* name, const char* sig )
|
|
{
|
|
return getObjectFieldWithFID( env, container, name, sig, NULL );
|
|
}
|
|
|
|
void
|
|
setObjectField( JNIEnv* env, jobject obj, const char* name, const char* sig,
|
|
jobject val )
|
|
{
|
|
jfieldID fid = getFieldID( env, obj, name, sig );
|
|
XP_ASSERT( !!fid );
|
|
(*env)->SetObjectField( env, obj, fid, val );
|
|
deleteLocalRef( env, val );
|
|
}
|
|
|
|
bool
|
|
getBool( JNIEnv* env, jobject obj, const char* name )
|
|
{
|
|
bool result;
|
|
jfieldID fid = getFieldID( env, obj, name, "Z");
|
|
XP_ASSERT( !!fid );
|
|
result = (*env)->GetBooleanField( env, obj, fid );
|
|
return result;
|
|
}
|
|
|
|
void
|
|
getBools( JNIEnv* env, void* cobj, jobject jobj, const SetInfo* sis, XP_U16 nSis )
|
|
{
|
|
for ( int ii = 0; ii < nSis; ++ii ) {
|
|
const SetInfo* si = &sis[ii];
|
|
XP_Bool val = getBool( env, jobj, si->name );
|
|
*(XP_Bool*)(((uint8_t*)cobj)+si->offset) = val;
|
|
/* XP_LOGF( "%s: wrote bool %s with val %d at offset %d", __func__, */
|
|
/* si->name, val, si->offset ); */
|
|
}
|
|
}
|
|
|
|
jintArray
|
|
makeIntArray( JNIEnv* env, int count, const void* vals, size_t elemSize )
|
|
{
|
|
jintArray array = (*env)->NewIntArray( env, count );
|
|
XP_ASSERT( !!array );
|
|
jint* elems = (*env)->GetIntArrayElements( env, array, NULL );
|
|
XP_ASSERT( !!elems );
|
|
jint elem;
|
|
for ( int ii = 0; ii < count; ++ii ) {
|
|
switch( elemSize ) {
|
|
case sizeof(XP_U32):
|
|
elem = *(XP_U32*)vals;
|
|
break;
|
|
case sizeof(XP_U16):
|
|
elem = *(XP_U16*)vals;
|
|
break;
|
|
case sizeof(XP_U8):
|
|
elem = *(XP_U8*)vals;
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
break;
|
|
}
|
|
vals += elemSize;
|
|
elems[ii] = elem;
|
|
}
|
|
(*env)->ReleaseIntArrayElements( env, array, elems, 0 );
|
|
return array;
|
|
}
|
|
|
|
void
|
|
setIntArray( JNIEnv* env, jobject jowner, const char* fieldName,
|
|
int count, const void* vals, size_t elemSize )
|
|
{
|
|
jintArray jarr = makeIntArray( env, count, vals, elemSize );
|
|
setObjectField( env, jowner, fieldName, "[I", jarr );
|
|
}
|
|
|
|
jbyteArray
|
|
makeByteArray( JNIEnv* env, int siz, const jbyte* vals )
|
|
{
|
|
jbyteArray array = (*env)->NewByteArray( env, siz );
|
|
XP_ASSERT( !!array );
|
|
if ( !!vals ) {
|
|
jbyte* elems = (*env)->GetByteArrayElements( env, array, NULL );
|
|
XP_ASSERT( !!elems );
|
|
XP_MEMCPY( elems, vals, siz * sizeof(*elems) );
|
|
(*env)->ReleaseByteArrayElements( env, array, elems, 0 );
|
|
}
|
|
return array;
|
|
}
|
|
|
|
jbyteArray
|
|
streamToBArray( JNIEnv* env, XWStreamCtxt* stream )
|
|
{
|
|
int nBytes = stream_getSize( stream );
|
|
jbyteArray result = (*env)->NewByteArray( env, nBytes );
|
|
jbyte* jelems = (*env)->GetByteArrayElements( env, result, NULL );
|
|
stream_getBytes( stream, jelems, nBytes );
|
|
(*env)->ReleaseByteArrayElements( env, result, jelems, 0 );
|
|
return result;
|
|
}
|
|
|
|
void
|
|
setBoolArray( JNIEnv* env, jbooleanArray jarr, int count,
|
|
const jboolean* vals )
|
|
{
|
|
jboolean* elems = (*env)->GetBooleanArrayElements( env, jarr, NULL );
|
|
XP_ASSERT( !!elems );
|
|
XP_MEMCPY( elems, vals, count * sizeof(*elems) );
|
|
(*env)->ReleaseBooleanArrayElements( env, jarr, elems, 0 );
|
|
}
|
|
|
|
jbooleanArray
|
|
makeBooleanArray( JNIEnv* env, int siz, const jboolean* vals )
|
|
{
|
|
jbooleanArray array = (*env)->NewBooleanArray( env, siz );
|
|
XP_ASSERT( !!array );
|
|
if ( !!vals ) {
|
|
setBoolArray( env, array, siz, vals );
|
|
}
|
|
return array;
|
|
}
|
|
|
|
void
|
|
getIntsFromArray( JNIEnv* env, int dest[], jintArray arr, int count, bool del )
|
|
{
|
|
jint* ints = (*env)->GetIntArrayElements(env, arr, 0);
|
|
for ( int ii = 0; ii < count; ++ii ) {
|
|
dest[ii] = ints[ii];
|
|
}
|
|
(*env)->ReleaseIntArrayElements( env, arr, ints, 0 );
|
|
if ( del ) {
|
|
deleteLocalRef( env, arr );
|
|
}
|
|
}
|
|
|
|
void
|
|
setIntInArray( JNIEnv* env, jintArray arr, int index, int val )
|
|
{
|
|
jint* ints = (*env)->GetIntArrayElements( env, arr, 0 );
|
|
#ifdef DEBUG
|
|
jsize len = (*env)->GetArrayLength( env, arr );
|
|
XP_ASSERT( len > index );
|
|
#endif
|
|
ints[index] = val;
|
|
(*env)->ReleaseIntArrayElements( env, arr, ints, 0 );
|
|
}
|
|
|
|
jobjectArray
|
|
makeStringArray( JNIEnv* env, const int count, const XP_UCHAR* const* vals )
|
|
{
|
|
jobjectArray jarray;
|
|
{
|
|
jclass clas = (*env)->FindClass(env, "java/lang/String");
|
|
jstring empty = (*env)->NewStringUTF( env, "" );
|
|
jarray = (*env)->NewObjectArray( env, count, clas, empty );
|
|
deleteLocalRefs( env, clas, empty, DELETE_NO_REF );
|
|
}
|
|
|
|
for ( int ii = 0; !!vals && ii < count; ++ii ) {
|
|
jstring jstr = (*env)->NewStringUTF( env, vals[ii] );
|
|
(*env)->SetObjectArrayElement( env, jarray, ii, jstr );
|
|
deleteLocalRef( env, jstr );
|
|
}
|
|
|
|
return jarray;
|
|
}
|
|
|
|
void
|
|
setStringArray( JNIEnv* env, jobject jowner, const char* ownerField,
|
|
int count, const XP_UCHAR** vals )
|
|
{
|
|
jobjectArray jaddrs = makeStringArray( env, count, vals );
|
|
setObjectField( env, jowner, ownerField, "[Ljava/lang/String;", jaddrs );
|
|
}
|
|
|
|
jobjectArray
|
|
makeByteArrayArray( JNIEnv* env, int siz )
|
|
{
|
|
jclass clas = (*env)->FindClass( env, "[B" );
|
|
jobjectArray result = (*env)->NewObjectArray( env, siz, clas, NULL );
|
|
deleteLocalRef( env, clas );
|
|
return result;
|
|
}
|
|
|
|
jstring
|
|
streamToJString( JNIEnv* env, XWStreamCtxt* stream )
|
|
{
|
|
int len = stream_getSize( stream );
|
|
XP_UCHAR buf[1 + len];
|
|
stream_getBytes( stream, buf, len );
|
|
buf[len] = '\0';
|
|
|
|
jstring jstr = (*env)->NewStringUTF( env, buf );
|
|
|
|
return jstr;
|
|
}
|
|
|
|
jmethodID
|
|
getMethodID( JNIEnv* env, jobject obj, const char* proc, const char* sig )
|
|
{
|
|
XP_ASSERT( !!env );
|
|
jclass cls = (*env)->GetObjectClass( env, obj );
|
|
XP_ASSERT( !!cls );
|
|
#ifdef DEBUG
|
|
char buf[128] = {0};
|
|
/* int len = sizeof(buf); */
|
|
/* getClassName( env, obj, buf, &len ); */
|
|
#endif
|
|
jmethodID mid = (*env)->GetMethodID( env, cls, proc, sig );
|
|
if ( !mid ) {
|
|
XP_LOGFF( "no mid for proc %s, sig %s in object of class %s",
|
|
proc, sig, buf );
|
|
}
|
|
XP_ASSERT( !!mid );
|
|
deleteLocalRef( env, cls );
|
|
return mid;
|
|
}
|
|
|
|
void
|
|
setTypeSetFieldIn( JNIEnv* env, const CommsAddrRec* addr, jobject jTarget,
|
|
const char* fieldName )
|
|
{
|
|
jobject jtypset = addrTypesToJ( env, addr );
|
|
XP_ASSERT( !!jtypset );
|
|
setObjectField( env, jTarget, fieldName,
|
|
"L" PKG_PATH("jni/CommsAddrRec$CommsConnTypeSet") ";",
|
|
jtypset );
|
|
}
|
|
|
|
jobject
|
|
makeObject( JNIEnv* env, const char* className, const char* initSig, ... )
|
|
{
|
|
jclass clazz = (*env)->FindClass( env, className );
|
|
XP_ASSERT( !!clazz );
|
|
jmethodID mid = (*env)->GetMethodID( env, clazz, "<init>", initSig );
|
|
XP_ASSERT( !!mid );
|
|
|
|
va_list ap;
|
|
va_start( ap, initSig );
|
|
jobject result = (*env)->NewObjectV( env, clazz, mid, ap );
|
|
va_end( ap );
|
|
|
|
deleteLocalRef( env, clazz );
|
|
return result;
|
|
}
|
|
|
|
jobject
|
|
makeObjectEmptyConstr( JNIEnv* env, const char* className )
|
|
{
|
|
return makeObject( env, className, "()V" );
|
|
}
|
|
|
|
jobject
|
|
makeJAddr( JNIEnv* env, const CommsAddrRec* addr )
|
|
{
|
|
jobject jaddr = NULL;
|
|
if ( NULL != addr ) {
|
|
jaddr = makeObjectEmptyConstr( env, PKG_PATH("jni/CommsAddrRec") );
|
|
setJAddrRec( env, jaddr, addr );
|
|
}
|
|
return jaddr;
|
|
}
|
|
|
|
/* Copy C object data into Java object */
|
|
jobject
|
|
setJAddrRec( JNIEnv* env, jobject jaddr, const CommsAddrRec* addr )
|
|
{
|
|
XP_ASSERT( !!addr );
|
|
setTypeSetFieldIn( env, addr, jaddr, "conTypes" );
|
|
|
|
CommsConnType typ;
|
|
for ( XP_U32 st = 0; addr_iter( addr, &typ, &st ); ) {
|
|
switch ( typ ) {
|
|
case COMMS_CONN_NONE:
|
|
break;
|
|
#ifdef XWFEATURE_RELAY
|
|
case COMMS_CONN_RELAY:
|
|
setInt( env, jaddr, "ip_relay_port", addr->u.ip_relay.port );
|
|
setString( env, jaddr, "ip_relay_hostName", addr->u.ip_relay.hostName );
|
|
setString( env, jaddr, "ip_relay_invite", addr->u.ip_relay.invite );
|
|
setBool( env, jaddr, "ip_relay_seeksPublicRoom",
|
|
addr->u.ip_relay.seeksPublicRoom );
|
|
setBool( env, jaddr, "ip_relay_advertiseRoom",
|
|
addr->u.ip_relay.advertiseRoom );
|
|
break;
|
|
#endif
|
|
case COMMS_CONN_SMS:
|
|
setString( env, jaddr, "sms_phone", addr->u.sms.phone );
|
|
setInt( env, jaddr, "sms_port", addr->u.sms.port );
|
|
break;
|
|
case COMMS_CONN_BT:
|
|
setString( env, jaddr, "bt_hostName", addr->u.bt.hostName );
|
|
setString( env, jaddr, "bt_btAddr", addr->u.bt.btAddr.chars );
|
|
break;
|
|
case COMMS_CONN_P2P:
|
|
setString( env, jaddr, "p2p_addr", addr->u.p2p.mac_addr );
|
|
break;
|
|
case COMMS_CONN_NFC:
|
|
break;
|
|
case COMMS_CONN_MQTT: {
|
|
XP_UCHAR buf[32];
|
|
formatMQTTDevID( &addr->u.mqtt.devID, buf, VSIZE(buf) );
|
|
setString( env, jaddr, "mqtt_devID", buf );
|
|
}
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
}
|
|
return jaddr;
|
|
}
|
|
|
|
jobject
|
|
addrTypesToJ( JNIEnv* env, const CommsAddrRec* addr )
|
|
{
|
|
XP_ASSERT( !!addr );
|
|
jobject result =
|
|
makeObjectEmptyConstr( env, PKG_PATH("jni/CommsAddrRec$CommsConnTypeSet") );
|
|
XP_ASSERT( !!result );
|
|
|
|
jmethodID mid2 = getMethodID( env, result, "add",
|
|
"(Ljava/lang/Object;)Z" );
|
|
XP_ASSERT( !!mid2 );
|
|
CommsConnType typ;
|
|
for ( XP_U32 st = 0; addr_iter( addr, &typ, &st ); ) {
|
|
jobject jtyp = intToJEnum( env, typ,
|
|
PKG_PATH("jni/CommsAddrRec$CommsConnType") );
|
|
XP_ASSERT( !!jtyp );
|
|
(*env)->CallBooleanMethod( env, result, mid2, jtyp );
|
|
deleteLocalRef( env, jtyp );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
jobjectArray
|
|
makeAddrArray( JNIEnv* env, XP_U16 count, const CommsAddrRec* addrs )
|
|
{
|
|
jclass clas = (*env)->FindClass( env, PKG_PATH("jni/CommsAddrRec") );
|
|
jobjectArray result = (*env)->NewObjectArray( env, count, clas, NULL );
|
|
for ( int ii = 0; ii < count; ++ii ) {
|
|
jobject jaddr = makeJAddr( env, &addrs[ii] );
|
|
(*env)->SetObjectArrayElement( env, result, ii, jaddr );
|
|
deleteLocalRef( env, jaddr );
|
|
}
|
|
deleteLocalRef( env, clas );
|
|
return result;
|
|
}
|
|
|
|
/* Writes a java version of CommsAddrRec into a C one */
|
|
void
|
|
getJAddrRec( JNIEnv* env, CommsAddrRec* addr, jobject jaddr )
|
|
{
|
|
XP_MEMSET( addr, 0, sizeof(*addr) );
|
|
/* Iterate over types in the set in jaddr, and for each call
|
|
addr_addType() and then copy in the types. */
|
|
jobject jtypeset = getObjectField( env, jaddr, "conTypes",
|
|
"L" PKG_PATH("jni/CommsAddrRec$CommsConnTypeSet") ";" );
|
|
XP_ASSERT( !!jtypeset );
|
|
jmethodID mid = getMethodID( env, jtypeset, "getTypes",
|
|
"()[L" PKG_PATH("jni/CommsAddrRec$CommsConnType;") );
|
|
XP_ASSERT( !!mid );
|
|
jobject jtypesarr = (*env)->CallObjectMethod( env, jtypeset, mid );
|
|
XP_ASSERT( !!jtypesarr );
|
|
jsize len = (*env)->GetArrayLength( env, jtypesarr );
|
|
for ( int ii = 0; ii < len; ++ii ) {
|
|
jobject jtype = (*env)->GetObjectArrayElement( env, jtypesarr, ii );
|
|
jint asInt = jEnumToInt( env, jtype );
|
|
deleteLocalRef( env, jtype );
|
|
CommsConnType typ = (CommsConnType)asInt;
|
|
|
|
addr_addType( addr, typ );
|
|
|
|
switch ( typ ) {
|
|
case COMMS_CONN_RELAY:
|
|
#ifdef XWFEATURE_RELAY
|
|
addr->u.ip_relay.port = getInt( env, jaddr, "ip_relay_port" );
|
|
getString( env, jaddr, "ip_relay_hostName", addr->u.ip_relay.hostName,
|
|
VSIZE(addr->u.ip_relay.hostName) );
|
|
getString( env, jaddr, "ip_relay_invite", addr->u.ip_relay.invite,
|
|
VSIZE(addr->u.ip_relay.invite) );
|
|
addr->u.ip_relay.seeksPublicRoom =
|
|
getBool( env, jaddr, "ip_relay_seeksPublicRoom" );
|
|
addr->u.ip_relay.advertiseRoom =
|
|
getBool( env, jaddr, "ip_relay_advertiseRoom" );
|
|
|
|
#endif
|
|
break;
|
|
case COMMS_CONN_SMS:
|
|
getString( env, jaddr, "sms_phone", addr->u.sms.phone,
|
|
VSIZE(addr->u.sms.phone) );
|
|
// XP_LOGF( "%s: got SMS; phone=%s", __func__, addr->u.sms.phone );
|
|
addr->u.sms.port = getInt( env, jaddr, "sms_port" );
|
|
break;
|
|
case COMMS_CONN_BT:
|
|
getString( env, jaddr, "bt_hostName", addr->u.bt.hostName,
|
|
VSIZE(addr->u.bt.hostName) );
|
|
getString( env, jaddr, "bt_btAddr", addr->u.bt.btAddr.chars,
|
|
VSIZE(addr->u.bt.btAddr.chars) );
|
|
break;
|
|
case COMMS_CONN_P2P:
|
|
getString( env, jaddr, "p2p_addr", addr->u.p2p.mac_addr,
|
|
VSIZE(addr->u.p2p.mac_addr) );
|
|
break;
|
|
case COMMS_CONN_NFC:
|
|
break;
|
|
case COMMS_CONN_MQTT: {
|
|
XP_UCHAR buf[32];
|
|
getString( env, jaddr, "mqtt_devID", buf, VSIZE(buf) );
|
|
sscanf( buf, MQTTDevID_FMT, &addr->u.mqtt.devID );
|
|
}
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
}
|
|
deleteLocalRefs( env, jtypeset, jtypesarr, DELETE_NO_REF );
|
|
}
|
|
|
|
jint
|
|
jenumFieldToInt( JNIEnv* env, jobject j_gi, const char* field,
|
|
const char* fieldSig )
|
|
{
|
|
char sig[128];
|
|
snprintf( sig, sizeof(sig), "L%s;", fieldSig );
|
|
jobject jenum = getObjectField( env, j_gi, field, sig );
|
|
XP_ASSERT( !!jenum );
|
|
jint result = jEnumToInt( env, jenum );
|
|
|
|
deleteLocalRef( env, jenum );
|
|
return result;
|
|
}
|
|
|
|
void
|
|
intToJenumField( JNIEnv* env, jobject jobj, int val, const char* field,
|
|
const char* fieldSig )
|
|
{
|
|
char buf[128];
|
|
snprintf( buf, sizeof(buf), "L%s;", fieldSig );
|
|
|
|
jfieldID fid;
|
|
jobject jenum = getObjectFieldWithFID( env, jobj, field, buf, &fid );
|
|
if ( !jenum ) { /* won't exist in new object */
|
|
jenum = makeObjectEmptyConstr( env, fieldSig );
|
|
XP_ASSERT( !!jenum );
|
|
(*env)->SetObjectField( env, jobj, fid, jenum );
|
|
}
|
|
|
|
jobject jval = intToJEnum( env, val, fieldSig );
|
|
XP_ASSERT( !!jval );
|
|
(*env)->SetObjectField( env, jobj, fid, jval );
|
|
deleteLocalRef( env, jval );
|
|
} /* intToJenumField */
|
|
|
|
/* Cons up a new enum instance and set its value */
|
|
jobject
|
|
intToJEnum( JNIEnv* env, int val, const char* enumSig )
|
|
{
|
|
jobject jenum = NULL;
|
|
jclass clazz = (*env)->FindClass( env, enumSig );
|
|
XP_ASSERT( !!clazz );
|
|
|
|
char buf[128];
|
|
snprintf( buf, sizeof(buf), "()[L%s;", enumSig );
|
|
jmethodID mid = (*env)->GetStaticMethodID( env, clazz, "values", buf );
|
|
XP_ASSERT( !!mid );
|
|
|
|
jobject jvalues = (*env)->CallStaticObjectMethod( env, clazz, mid );
|
|
XP_ASSERT( !!jvalues );
|
|
XP_ASSERT( val < (*env)->GetArrayLength( env, jvalues ) );
|
|
/* get the value we want */
|
|
jenum = (*env)->GetObjectArrayElement( env, jvalues, val );
|
|
XP_ASSERT( !!jenum );
|
|
|
|
deleteLocalRefs( env, jvalues, clazz, DELETE_NO_REF );
|
|
return jenum;
|
|
} /* intToJEnum */
|
|
|
|
jint
|
|
jEnumToInt( JNIEnv* env, jobject jenum )
|
|
{
|
|
jmethodID mid = getMethodID( env, jenum, "ordinal", "()I" );
|
|
XP_ASSERT( !!mid );
|
|
return (*env)->CallIntMethod( env, jenum, mid );
|
|
}
|
|
|
|
static const SetInfo nli_ints[] = {
|
|
ARR_MEMBER( NetLaunchInfo, _conTypes ),
|
|
ARR_MEMBER( NetLaunchInfo, forceChannel ),
|
|
ARR_MEMBER( NetLaunchInfo, nPlayersT ),
|
|
ARR_MEMBER( NetLaunchInfo, nPlayersH ),
|
|
ARR_MEMBER( NetLaunchInfo, gameID ),
|
|
ARR_MEMBER( NetLaunchInfo, osVers ),
|
|
};
|
|
|
|
static const SetInfo nli_bools[] = {
|
|
ARR_MEMBER( NetLaunchInfo, isGSM ),
|
|
ARR_MEMBER( NetLaunchInfo, remotesAreRobots ),
|
|
};
|
|
|
|
static const SetInfo nli_strs[] = {
|
|
ARR_MEMBER( NetLaunchInfo, dict ),
|
|
ARR_MEMBER( NetLaunchInfo, isoCodeStr ),
|
|
ARR_MEMBER( NetLaunchInfo, gameName ),
|
|
ARR_MEMBER( NetLaunchInfo, room ),
|
|
ARR_MEMBER( NetLaunchInfo, btName ),
|
|
ARR_MEMBER( NetLaunchInfo, btAddress ),
|
|
ARR_MEMBER( NetLaunchInfo, phone ),
|
|
ARR_MEMBER( NetLaunchInfo, inviteID ),
|
|
ARR_MEMBER( NetLaunchInfo, mqttDevID ),
|
|
};
|
|
|
|
void
|
|
loadNLI( JNIEnv* env, NetLaunchInfo* nli, jobject jnli )
|
|
{
|
|
XP_MEMSET( nli, 0, sizeof(*nli) );
|
|
getInts( env, (void*)nli, jnli, AANDS(nli_ints) );
|
|
getBools( env, (void*)nli, jnli, AANDS(nli_bools) );
|
|
getStrings( env, (void*)nli, jnli, AANDS(nli_strs) );
|
|
}
|
|
|
|
void
|
|
setNLI( JNIEnv* env, jobject jnli, const NetLaunchInfo* nli )
|
|
{
|
|
setInts( env, jnli, (void*)nli, AANDS(nli_ints) );
|
|
setBools( env, jnli, (void*)nli, AANDS(nli_bools) );
|
|
setStrings( env, jnli, (void*)nli, AANDS(nli_strs) );
|
|
}
|
|
|
|
XWStreamCtxt*
|
|
and_empty_stream( MPFORMAL AndGameGlobals* globals )
|
|
{
|
|
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
|
|
globals, 0, NULL, NULL );
|
|
return stream;
|
|
}
|
|
|
|
XP_U32
|
|
getCurSeconds( JNIEnv* env )
|
|
{
|
|
jclass clazz = (*env)->FindClass( env, PKG_PATH("Utils") );
|
|
XP_ASSERT( !!clazz );
|
|
jmethodID mid = (*env)->GetStaticMethodID( env, clazz,
|
|
"getCurSeconds", "()J" );
|
|
jlong result = (*env)->CallStaticLongMethod( env, clazz, mid );
|
|
|
|
deleteLocalRef( env, clazz );
|
|
return result;
|
|
}
|
|
|
|
void deleteLocalRef( JNIEnv* env, jobject jobj )
|
|
{
|
|
if ( NULL != jobj ) {
|
|
(*env)->DeleteLocalRef( env, jobj );
|
|
}
|
|
}
|
|
|
|
void
|
|
deleteLocalRefs( JNIEnv* env, ... )
|
|
{
|
|
va_list ap;
|
|
va_start( ap, env );
|
|
for ( ; ; ) {
|
|
jobject jnext = va_arg( ap, jobject );
|
|
if ( DELETE_NO_REF == jnext ) {
|
|
break;
|
|
}
|
|
deleteLocalRef( env, jnext );
|
|
}
|
|
va_end( ap );
|
|
}
|
|
|
|
/* Passed to device methods related to MQTT messages */
|
|
void
|
|
msgAndTopicProc( void* closure, const XP_UCHAR* topic,
|
|
const XP_U8* msgBuf, XP_U16 msgLen )
|
|
{
|
|
MTPData* mtp = (MTPData*)closure;
|
|
JNIEnv* env = mtp->env;
|
|
|
|
if ( VSIZE(mtp->topics) <= mtp->count ) {
|
|
XP_LOGFF( "exausted space for topics; dropping" );
|
|
} else {
|
|
const XP_UCHAR* ptr = &mtp->storage[mtp->offset];
|
|
size_t siz = XP_SNPRINTF( (char*)ptr, VSIZE(mtp->storage) - mtp->offset,
|
|
"%s", topic );
|
|
if ( siz >= VSIZE(mtp->storage) - mtp->offset ) {
|
|
XP_LOGFF( "exausted space for data; dropping" );
|
|
} else {
|
|
mtp->topics[mtp->count] = ptr;
|
|
mtp->offset += 1 + XP_STRLEN(ptr);
|
|
|
|
mtp->jPackets[mtp->count] = makeByteArray( env, msgLen, (const jbyte*)msgBuf );
|
|
|
|
++mtp->count;
|
|
XP_LOGFF( "mtp->count now: %d", mtp->count );
|
|
}
|
|
}
|
|
}
|
|
|
|
jobject
|
|
wrapResults( MTPData* mtp )
|
|
{
|
|
JNIEnv* env = mtp->env;
|
|
jobject result =
|
|
makeObjectEmptyConstr( env, PKG_PATH("jni/XwJNI$TopicsAndPackets"));
|
|
|
|
jobjectArray jTopics = makeStringArray( env, mtp->count, mtp->topics );
|
|
setObjectField( env, result, "topics", "[Ljava/lang/String;", jTopics );
|
|
|
|
jobjectArray jPackets = makeByteArrayArray( env, mtp->count );
|
|
for ( int ii = 0; ii < mtp->count; ++ii ) {
|
|
(*env)->SetObjectArrayElement( env, jPackets, ii, mtp->jPackets[ii] );
|
|
deleteLocalRef( env, mtp->jPackets[ii] );
|
|
}
|
|
setObjectField( env, result, "packets", "[[B", jPackets );
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* A bunch of threads are generating log statements. */
|
|
static void
|
|
passToJava( const char* tag, const char* msg )
|
|
{
|
|
JNIEnv* env = waitEnvFromGlobals();
|
|
if ( !!env ) {
|
|
jstring jtag = (*env)->NewStringUTF( env, tag );
|
|
jstring jbuf = (*env)->NewStringUTF( env, msg );
|
|
jclass clazz = (*env)->FindClass( env, PKG_PATH("Log") );
|
|
XP_ASSERT( !!clazz );
|
|
jmethodID mid = (*env)->GetStaticMethodID( env, clazz, "store",
|
|
"(Ljava/lang/String;Ljava/lang/String;)V" );
|
|
(*env)->CallStaticVoidMethod( env, clazz, mid, jtag, jbuf );
|
|
deleteLocalRefs( env, clazz, jtag, jbuf, DELETE_NO_REF );
|
|
} else {
|
|
// RAW_LOG( "env is NULL; dropping" );
|
|
}
|
|
}
|
|
|
|
static void
|
|
debugf( const char* format, va_list ap )
|
|
{
|
|
char buf[1024];
|
|
int len;
|
|
struct tm* timp;
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
|
|
gettimeofday( &tv, &tz );
|
|
timp = localtime( &tv.tv_sec );
|
|
|
|
len = snprintf( buf, sizeof(buf), "%.2d:%.2d:%.2d: ",
|
|
timp->tm_hour, timp->tm_min, timp->tm_sec );
|
|
if ( len < sizeof(buf) ) {
|
|
vsnprintf( buf + len, sizeof(buf)-len, format, ap );
|
|
}
|
|
|
|
const char* tag =
|
|
# if defined VARIANT_xw4GPlay || defined VARIANT_xw4fdroid || defined VARIANT_xw4Foss
|
|
"xw4"
|
|
# elif defined VARIANT_xw4d || defined VARIANT_xw4dGPlay
|
|
"x4bg"
|
|
# elif defined VARIANT_xw4dup || defined VARIANT_xw4dupGPlay
|
|
"x4du"
|
|
# else
|
|
ERROR
|
|
# endif
|
|
;
|
|
|
|
(void)__android_log_write( ANDROID_LOG_DEBUG, tag, buf );
|
|
|
|
passToJava( tag, buf );
|
|
}
|
|
|
|
void
|
|
raw_log( const char* func, const char* fmt, ... )
|
|
{
|
|
char buf[1024];
|
|
int len = snprintf( buf, VSIZE(buf) - 1, "in %s(): %s", func, fmt );
|
|
buf[len] = '\0';
|
|
|
|
va_list ap;
|
|
va_start( ap, fmt );
|
|
char buf2[1024];
|
|
len = vsnprintf( buf2, VSIZE(buf2) - 1, buf, ap );
|
|
va_end( ap );
|
|
|
|
(void)__android_log_write( ANDROID_LOG_DEBUG, "raw", buf2 );
|
|
}
|
|
|
|
void
|
|
android_debugf( const char* format, ... )
|
|
{
|
|
va_list ap;
|
|
va_start( ap, format );
|
|
debugf( format, ap );
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
android_debugff(const char* func, const char* file, const char* fmt, ...)
|
|
{
|
|
char buf[256];
|
|
const char* shortPath = 1 + strrchr(file, '/');
|
|
if ( !shortPath ) {
|
|
shortPath = file;
|
|
}
|
|
snprintf( buf, sizeof(buf), "%s:%s(): %s", shortPath, func, fmt );
|
|
|
|
va_list ap;
|
|
va_start( ap, fmt );
|
|
debugf( buf, ap );
|
|
va_end( ap );
|
|
}
|
|
|
|
/* Print an object's class name into buffer.
|
|
*
|
|
* NOTE: this must be called in advance of any jni error, because methods on
|
|
* env can't be called once there's an exception pending.
|
|
*/
|
|
#if 0
|
|
static void
|
|
getClassName( JNIEnv* env, jobject obj, char* out, int* outLen )
|
|
{
|
|
XP_ASSERT( !!obj );
|
|
jclass cls1 = (*env)->GetObjectClass( env, obj );
|
|
|
|
// First get the class object
|
|
jmethodID mid = (*env)->GetMethodID( env, cls1, "getClass",
|
|
"()Ljava/lang/Class;" );
|
|
jobject clsObj = (*env)->CallObjectMethod( env, obj, mid );
|
|
|
|
// Now get the class object's class descriptor
|
|
jclass cls2 = (*env)->GetObjectClass( env, clsObj );
|
|
// Find the getName() method on the class object
|
|
mid = (*env)->GetMethodID( env, cls2, "getName", "()Ljava/lang/String;" );
|
|
|
|
// Call the getName() to get a jstring object back
|
|
jstring strObj = (jstring)(*env)->CallObjectMethod( env, clsObj, mid );
|
|
|
|
jint slen = (*env)->GetStringUTFLength( env, strObj );
|
|
if ( slen < *outLen ) {
|
|
*outLen = slen;
|
|
(*env)->GetStringUTFRegion( env, strObj, 0, slen, out );
|
|
out[slen] = '\0';
|
|
} else {
|
|
*outLen = 0;
|
|
out[0] = '\0';
|
|
}
|
|
deleteLocalRefs( env, clsObj, cls1, cls2, strObj, DELETE_NO_REF );
|
|
LOG_RETURNF( "%s", out );
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/* #ifdef DEBUG */
|
|
/* XP_U32 */
|
|
/* andy_rand( const char* caller ) */
|
|
/* { */
|
|
/* XP_U32 result = rand(); */
|
|
/* XP_LOGF( "%s: returning 0x%lx to %s", __func__, result, caller ); */
|
|
/* LOG_RETURNF( "%lx", result ); */
|
|
/* return result; */
|
|
/* } */
|
|
/* #endif */
|
|
|
|
#ifndef MEM_DEBUG
|
|
void
|
|
and_freep( void** ptrp )
|
|
{
|
|
if ( !!*ptrp ) {
|
|
free( *ptrp );
|
|
*ptrp = NULL;
|
|
}
|
|
}
|
|
#endif
|