src/hidapi/android/hid.cpp
author Sam Lantinga <slouken@libsdl.org>
Mon, 08 Oct 2018 12:49:30 -0700
changeset 12308 4e3b4ddb8787
parent 12242 df7260f149f2
child 12346 b4c98c84a0b2
permissions -rw-r--r--
Fixed life-cycle issues with two activities sharing HIDDeviceManager
     1 //=================== Copyright Valve Corporation, All rights reserved. =======
     2 //
     3 // Purpose: A wrapper implementing "HID" API for Android
     4 //
     5 //          This layer glues the hidapi API to Android's USB and BLE stack.
     6 //
     7 //=============================================================================
     8 
     9 #include <jni.h>
    10 #include <android/log.h>
    11 #include <pthread.h>
    12 #include <errno.h>	// For ETIMEDOUT and ECONNRESET
    13 #include <stdlib.h> // For malloc() and free()
    14 #include <string.h>	// For memcpy()
    15 
    16 #define TAG "hidapi"
    17 #ifdef DEBUG
    18 #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
    19 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
    20 #else
    21 #define LOGV(...)
    22 #define LOGD(...)
    23 #endif
    24 
    25 #define SDL_JAVA_PREFIX                                 org_libsdl_app
    26 #define CONCAT1(prefix, class, function)                CONCAT2(prefix, class, function)
    27 #define CONCAT2(prefix, class, function)                Java_ ## prefix ## _ ## class ## _ ## function
    28 #define HID_DEVICE_MANAGER_JAVA_INTERFACE(function)     CONCAT1(SDL_JAVA_PREFIX, HIDDeviceManager, function)
    29 
    30 #include "../hidapi/hidapi.h"
    31 
    32 typedef uint32_t uint32;
    33 typedef uint64_t uint64;
    34 
    35 
    36 struct hid_device_
    37 {
    38 	int m_nId;
    39 	int m_nDeviceRefCount;
    40 };
    41 
    42 static JavaVM *g_JVM;
    43 static pthread_key_t g_ThreadKey;
    44 
    45 template<class T>
    46 class hid_device_ref
    47 {
    48 public:
    49 	hid_device_ref( T *pObject = nullptr ) : m_pObject( nullptr )
    50 	{
    51 		SetObject( pObject );
    52 	}
    53 
    54 	hid_device_ref( const hid_device_ref &rhs ) : m_pObject( nullptr )
    55 	{
    56 		SetObject( rhs.GetObject() );
    57 	}
    58 
    59 	~hid_device_ref()
    60 	{
    61 		SetObject( nullptr );
    62 	}
    63 
    64 	void SetObject( T *pObject )
    65 	{
    66 		if ( m_pObject && m_pObject->DecrementRefCount() == 0 )
    67 		{
    68 			delete m_pObject;
    69 		}
    70 
    71 		m_pObject = pObject;
    72 
    73 		if ( m_pObject )
    74 		{
    75 			m_pObject->IncrementRefCount();
    76 		}
    77 	}
    78 
    79 	hid_device_ref &operator =( T *pObject )
    80 	{
    81 		SetObject( pObject );
    82 		return *this;
    83 	}
    84 
    85 	hid_device_ref &operator =( const hid_device_ref &rhs )
    86 	{
    87 		SetObject( rhs.GetObject() );
    88 		return *this;
    89 	}
    90 
    91 	T *GetObject() const
    92 	{
    93 		return m_pObject;
    94 	}
    95 
    96 	T* operator->() const
    97 	{
    98 		return m_pObject;
    99 	}
   100 
   101 	operator bool() const
   102 	{
   103 		return ( m_pObject != nullptr );
   104 	}
   105 
   106 private:
   107 	T *m_pObject;
   108 };
   109 
   110 class hid_mutex_guard
   111 {
   112 public:
   113 	hid_mutex_guard( pthread_mutex_t *pMutex ) : m_pMutex( pMutex )
   114 	{
   115 		pthread_mutex_lock( m_pMutex );
   116 	}
   117 	~hid_mutex_guard()
   118 	{
   119 		pthread_mutex_unlock( m_pMutex );
   120 	}
   121 
   122 private:
   123 	pthread_mutex_t *m_pMutex;
   124 };
   125 
   126 class hid_buffer
   127 {
   128 public:
   129 	hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
   130 	{
   131 	}
   132 
   133 	hid_buffer( const uint8_t *pData, size_t nSize ) : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
   134 	{
   135 		assign( pData, nSize );
   136 	}
   137 
   138 	~hid_buffer()
   139 	{
   140 		delete[] m_pData;
   141 	}
   142 
   143 	void assign( const uint8_t *pData, size_t nSize )
   144 	{
   145 		if ( nSize > m_nAllocated )
   146 		{
   147 			delete[] m_pData;
   148 			m_pData = new uint8_t[ nSize ];
   149 			m_nAllocated = nSize;
   150 		}
   151 
   152 		m_nSize = nSize;
   153 		memcpy( m_pData, pData, nSize );
   154 	}
   155 
   156 	void clear()
   157 	{
   158 		m_nSize = 0;
   159 	}
   160 
   161 	size_t size() const
   162 	{
   163 		return m_nSize;
   164 	}
   165 
   166 	const uint8_t *data() const
   167 	{
   168 		return m_pData;
   169 	}
   170 
   171 private:
   172 	uint8_t *m_pData;
   173 	size_t m_nSize;
   174 	size_t m_nAllocated;
   175 };
   176 
   177 class hid_buffer_pool
   178 {
   179 public:
   180 	hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
   181 	{
   182 	}
   183 
   184 	~hid_buffer_pool()
   185 	{
   186 		clear();
   187 
   188 		while ( m_pFree )
   189 		{
   190 			hid_buffer_entry *pEntry = m_pFree;
   191 			m_pFree = m_pFree->m_pNext;
   192 			delete pEntry;
   193 		}
   194 	}
   195 
   196 	size_t size() const { return m_nSize; }
   197 
   198 	const hid_buffer &front() const { return m_pHead->m_buffer; }
   199 
   200 	void pop_front()
   201 	{
   202 		hid_buffer_entry *pEntry = m_pHead;
   203 		if ( pEntry )
   204 		{
   205 			m_pHead = pEntry->m_pNext;
   206 			if ( !m_pHead )
   207 			{
   208 				m_pTail = nullptr;
   209 			}
   210 			pEntry->m_pNext = m_pFree;
   211 			m_pFree = pEntry;
   212 			--m_nSize;
   213 		}
   214 	}
   215 
   216 	void emplace_back( const uint8_t *pData, size_t nSize )
   217 	{
   218 		hid_buffer_entry *pEntry;
   219 
   220 		if ( m_pFree )
   221 		{
   222 			pEntry = m_pFree;
   223 			m_pFree = m_pFree->m_pNext;
   224 		}
   225 		else
   226 		{
   227 			pEntry = new hid_buffer_entry;
   228 		}
   229 		pEntry->m_pNext = nullptr;
   230 
   231 		if ( m_pTail )
   232 		{
   233 			m_pTail->m_pNext = pEntry;
   234 		}
   235 		else
   236 		{
   237 			m_pHead = pEntry;
   238 		}
   239 		m_pTail = pEntry;
   240 
   241 		pEntry->m_buffer.assign( pData, nSize );
   242 		++m_nSize;
   243 	}
   244 
   245 	void clear()
   246 	{
   247 		while ( size() > 0 )
   248 		{
   249 			pop_front();
   250 		}
   251 	}
   252 
   253 private:
   254 	struct hid_buffer_entry
   255 	{
   256 		hid_buffer m_buffer;
   257 		hid_buffer_entry *m_pNext;
   258 	};
   259 
   260 	size_t m_nSize;
   261 	hid_buffer_entry *m_pHead;
   262 	hid_buffer_entry *m_pTail;
   263 	hid_buffer_entry *m_pFree;
   264 };
   265 
   266 static jbyteArray NewByteArray( JNIEnv* env, const uint8_t *pData, size_t nDataLen )
   267 {
   268 	jbyteArray array = env->NewByteArray( nDataLen );
   269 	jbyte *pBuf = env->GetByteArrayElements( array, NULL );
   270 	memcpy( pBuf, pData, nDataLen );
   271 	env->ReleaseByteArrayElements( array, pBuf, 0 );
   272 
   273 	return array;
   274 }
   275 
   276 static char *CreateStringFromJString( JNIEnv *env, const jstring &sString )
   277 {
   278 	size_t nLength = env->GetStringUTFLength( sString );
   279 	const char *pjChars = env->GetStringUTFChars( sString, NULL );
   280 	char *psString = (char*)malloc( nLength + 1 );
   281 	memcpy( psString, pjChars, nLength );
   282 	psString[ nLength ] = '\0';
   283 	env->ReleaseStringUTFChars( sString, pjChars );
   284 	return psString;
   285 }
   286 
   287 static wchar_t *CreateWStringFromJString( JNIEnv *env, const jstring &sString )
   288 {
   289 	size_t nLength = env->GetStringLength( sString );
   290 	const jchar *pjChars = env->GetStringChars( sString, NULL );
   291 	wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
   292 	wchar_t *pwChars = pwString;
   293 	for ( size_t iIndex = 0; iIndex < nLength; ++iIndex )
   294 	{
   295 		pwChars[ iIndex ] = pjChars[ iIndex ];
   296 	}
   297 	pwString[ nLength ] = '\0';
   298 	env->ReleaseStringChars( sString, pjChars );
   299 	return pwString;
   300 }
   301 
   302 static wchar_t *CreateWStringFromWString( const wchar_t *pwSrc )
   303 {
   304 	size_t nLength = wcslen( pwSrc );
   305 	wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
   306 	memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
   307 	pwString[ nLength ] = '\0';
   308 	return pwString;
   309 }
   310 
   311 static hid_device_info *CopyHIDDeviceInfo( const hid_device_info *pInfo )
   312 {
   313 	hid_device_info *pCopy = new hid_device_info;
   314 	*pCopy = *pInfo;
   315 	pCopy->path = strdup( pInfo->path );
   316 	pCopy->product_string = CreateWStringFromWString( pInfo->product_string );
   317 	pCopy->manufacturer_string = CreateWStringFromWString( pInfo->manufacturer_string );
   318 	pCopy->serial_number = CreateWStringFromWString( pInfo->serial_number );
   319 	return pCopy;
   320 }
   321 
   322 static void FreeHIDDeviceInfo( hid_device_info *pInfo )
   323 {
   324 	free( pInfo->path );
   325 	free( pInfo->serial_number );
   326 	free( pInfo->manufacturer_string );
   327 	free( pInfo->product_string );
   328 	delete pInfo;
   329 }
   330 
   331 static jclass  g_HIDDeviceManagerCallbackClass;
   332 static jobject g_HIDDeviceManagerCallbackHandler;
   333 static jmethodID g_midHIDDeviceManagerOpen;
   334 static jmethodID g_midHIDDeviceManagerSendOutputReport;
   335 static jmethodID g_midHIDDeviceManagerSendFeatureReport;
   336 static jmethodID g_midHIDDeviceManagerGetFeatureReport;
   337 static jmethodID g_midHIDDeviceManagerClose;
   338 
   339 static uint64_t get_timespec_ms( const struct timespec &ts )
   340 {
   341 	return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
   342 }
   343 
   344 class CHIDDevice
   345 {
   346 public:
   347 	CHIDDevice( int nDeviceID, hid_device_info *pInfo )
   348 	{
   349 		m_nId = nDeviceID;
   350 		m_pInfo = pInfo;
   351 
   352 		// The Bluetooth Steam Controller needs special handling
   353 		const int VALVE_USB_VID	= 0x28DE;
   354 		const int D0G_BLE2_PID = 0x1106;
   355 		if ( pInfo->vendor_id == VALVE_USB_VID && pInfo->product_id == D0G_BLE2_PID )
   356 		{
   357 			m_bIsBLESteamController = true;
   358 		}
   359 	}
   360 
   361 	~CHIDDevice()
   362 	{
   363 		FreeHIDDeviceInfo( m_pInfo );
   364 
   365 		// Note that we don't delete m_pDevice, as the app may still have a reference to it
   366 	}
   367 
   368 	int IncrementRefCount()
   369 	{
   370 		int nValue;
   371 		pthread_mutex_lock( &m_refCountLock );
   372 		nValue = ++m_nRefCount;
   373 		pthread_mutex_unlock( &m_refCountLock );
   374 		return nValue;
   375 	}
   376 
   377 	int DecrementRefCount()
   378 	{
   379 		int nValue;
   380 		pthread_mutex_lock( &m_refCountLock );
   381 		nValue = --m_nRefCount;
   382 		pthread_mutex_unlock( &m_refCountLock );
   383 		return nValue;
   384 	}
   385 
   386 	int GetId()
   387 	{
   388 		return m_nId;
   389 	}
   390 
   391 	const hid_device_info *GetDeviceInfo()
   392 	{
   393 		return m_pInfo;
   394 	}
   395 
   396 	hid_device *GetDevice()
   397 	{
   398 		return m_pDevice;
   399 	}
   400 
   401 	bool BOpen()
   402 	{
   403 		// Make sure thread is attached to JVM/env
   404 		JNIEnv *env;
   405 		g_JVM->AttachCurrentThread( &env, NULL );
   406 		pthread_setspecific( g_ThreadKey, (void*)env );
   407 
   408 		m_bIsWaitingForOpen = false;
   409 		m_bOpenResult = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerOpen, m_nId );
   410 
   411 		if ( m_bIsWaitingForOpen )
   412 		{
   413 			hid_mutex_guard cvl( &m_cvLock );
   414 
   415 			const int OPEN_TIMEOUT_SECONDS = 60;
   416 			struct timespec ts, endtime;
   417 			clock_gettime( CLOCK_REALTIME, &ts );
   418 			endtime = ts;
   419 			endtime.tv_sec += OPEN_TIMEOUT_SECONDS;
   420 			do
   421 			{
   422 				if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
   423 				{
   424 					break;
   425 				}
   426 			}
   427 			while ( m_bIsWaitingForOpen && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
   428 		}
   429 
   430 		if ( !m_bOpenResult )
   431 		{
   432 			if ( m_bIsWaitingForOpen )
   433 			{
   434 				LOGV( "Device open failed - timed out waiting for device permission" );
   435 			}
   436 			else
   437 			{
   438 				LOGV( "Device open failed" );
   439 			}
   440 			return false;
   441 		}
   442 
   443 		m_pDevice = new hid_device;
   444 		m_pDevice->m_nId = m_nId;
   445 		m_pDevice->m_nDeviceRefCount = 1;
   446 		LOGD("Creating device %d (%p), refCount = 1\n", m_pDevice->m_nId, m_pDevice);
   447 		return true;
   448 	}
   449 
   450 	void SetOpenPending()
   451 	{
   452 		m_bIsWaitingForOpen = true;
   453 	}
   454 
   455 	void SetOpenResult( bool bResult )
   456 	{
   457 		if ( m_bIsWaitingForOpen )
   458 		{
   459 			m_bOpenResult = bResult;
   460 			m_bIsWaitingForOpen = false;
   461 			pthread_cond_signal( &m_cv );
   462 		}
   463 	}
   464 
   465 	void ProcessInput( const uint8_t *pBuf, size_t nBufSize )
   466 	{
   467 		hid_mutex_guard l( &m_dataLock );
   468 
   469 		size_t MAX_REPORT_QUEUE_SIZE = 16;
   470 		if ( m_vecData.size() >= MAX_REPORT_QUEUE_SIZE )
   471 		{
   472 			m_vecData.pop_front();
   473 		}
   474 		m_vecData.emplace_back( pBuf, nBufSize );
   475 	}
   476 
   477 	int GetInput( unsigned char *data, size_t length )
   478 	{
   479 		hid_mutex_guard l( &m_dataLock );
   480 
   481 		if ( m_vecData.size() == 0 )
   482 		{
   483 //			LOGV( "hid_read_timeout no data available" );
   484 			return 0;
   485 		}
   486 
   487 		const hid_buffer &buffer = m_vecData.front();
   488 		size_t nDataLen = buffer.size() > length ? length : buffer.size();
   489 		if ( m_bIsBLESteamController )
   490 		{
   491 			data[0] = 0x03;
   492 			memcpy( data + 1, buffer.data(), nDataLen );
   493 			++nDataLen;
   494 		}
   495 		else
   496 		{
   497 			memcpy( data, buffer.data(), nDataLen );
   498 		}
   499 		m_vecData.pop_front();
   500 
   501 //		LOGV("Read %u bytes", nDataLen);
   502 //		LOGV("%02x %02x %02x %02x %02x %02x %02x %02x ....",
   503 //			 data[0], data[1], data[2], data[3],
   504 //			 data[4], data[5], data[6], data[7]);
   505 
   506 		return nDataLen;
   507 	}
   508 
   509 	int SendOutputReport( const unsigned char *pData, size_t nDataLen )
   510 	{
   511 		// Make sure thread is attached to JVM/env
   512 		JNIEnv *env;
   513 		g_JVM->AttachCurrentThread( &env, NULL );
   514 		pthread_setspecific( g_ThreadKey, (void*)env );
   515 
   516 		jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
   517 		int nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendOutputReport, m_nId, pBuf );
   518 		env->DeleteLocalRef( pBuf );
   519 		return nRet;
   520 	}
   521 
   522 	int SendFeatureReport( const unsigned char *pData, size_t nDataLen )
   523 	{
   524 		// Make sure thread is attached to JVM/env
   525 		JNIEnv *env;
   526 		g_JVM->AttachCurrentThread( &env, NULL );
   527 		pthread_setspecific( g_ThreadKey, (void*)env );
   528 
   529 		jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
   530 		int nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendFeatureReport, m_nId, pBuf );
   531 		env->DeleteLocalRef( pBuf );
   532 		return nRet;
   533 	}
   534 
   535 	void ProcessFeatureReport( const uint8_t *pBuf, size_t nBufSize )
   536 	{
   537 		hid_mutex_guard cvl( &m_cvLock );
   538 		if ( m_bIsWaitingForFeatureReport )
   539 		{
   540 			m_featureReport.assign( pBuf, nBufSize );
   541 
   542 			m_bIsWaitingForFeatureReport = false;
   543 			m_nFeatureReportError = 0;
   544 			pthread_cond_signal( &m_cv );
   545 		}
   546 	}
   547 
   548 	int GetFeatureReport( unsigned char *pData, size_t nDataLen )
   549 	{
   550 		// Make sure thread is attached to JVM/env
   551 		JNIEnv *env;
   552 		g_JVM->AttachCurrentThread( &env, NULL );
   553 		pthread_setspecific( g_ThreadKey, (void*)env );
   554 
   555 		{
   556 			hid_mutex_guard cvl( &m_cvLock );
   557 			if ( m_bIsWaitingForFeatureReport )
   558 			{
   559 				LOGV( "Get feature report already ongoing... bail" );
   560 				return -1; // Read already ongoing, we currently do not serialize, TODO
   561 			}
   562 			m_bIsWaitingForFeatureReport = true;
   563 		}
   564 
   565 		jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
   566 		int nRet = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerGetFeatureReport, m_nId, pBuf ) ? 0 : -1;
   567 		env->DeleteLocalRef( pBuf );
   568 		if ( nRet < 0 )
   569 		{
   570 			LOGV( "GetFeatureReport failed" );
   571 			m_bIsWaitingForFeatureReport = false;
   572 			return -1;
   573 		}
   574 
   575 		{
   576 			hid_mutex_guard cvl( &m_cvLock );
   577 			if ( m_bIsWaitingForFeatureReport )
   578 			{
   579 				LOGV("=== Going to sleep" );
   580 				// Wait in CV until we are no longer waiting for a feature report.
   581 				const int FEATURE_REPORT_TIMEOUT_SECONDS = 2;
   582 				struct timespec ts, endtime;
   583 				clock_gettime( CLOCK_REALTIME, &ts );
   584 				endtime = ts;
   585 				endtime.tv_sec += FEATURE_REPORT_TIMEOUT_SECONDS;
   586 				do
   587 				{
   588 					if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
   589 					{
   590 						break;
   591 					}
   592 				}
   593 				while ( m_bIsWaitingForFeatureReport && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
   594 
   595 				// We are back
   596 				if ( m_bIsWaitingForFeatureReport )
   597 				{
   598 					m_nFeatureReportError = -ETIMEDOUT;
   599 					m_bIsWaitingForFeatureReport = false;
   600 				}
   601 				LOGV( "=== Got feature report err=%d", m_nFeatureReportError );
   602 				if ( m_nFeatureReportError != 0 )
   603 				{
   604 					return m_nFeatureReportError;
   605 				}
   606 			}
   607 
   608 			size_t uBytesToCopy = m_featureReport.size() > nDataLen ? nDataLen : m_featureReport.size();
   609 			memcpy( pData, m_featureReport.data(), uBytesToCopy );
   610 			m_featureReport.clear();
   611 			LOGV( "=== Got %u bytes", uBytesToCopy );
   612 
   613 			return uBytesToCopy;
   614 		}
   615 	}
   616 
   617 	void Close( bool bDeleteDevice )
   618 	{
   619 		// Make sure thread is attached to JVM/env
   620 		JNIEnv *env;
   621 		g_JVM->AttachCurrentThread( &env, NULL );
   622 		pthread_setspecific( g_ThreadKey, (void*)env );
   623 
   624 		env->CallVoidMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerClose, m_nId );
   625 
   626 		hid_mutex_guard dataLock( &m_dataLock );
   627 		m_vecData.clear();
   628 
   629 		// Clean and release pending feature report reads
   630 		hid_mutex_guard cvLock( &m_cvLock );
   631 		m_featureReport.clear();
   632 		m_bIsWaitingForFeatureReport = false;
   633 		m_nFeatureReportError = -ECONNRESET;
   634 		pthread_cond_broadcast( &m_cv );
   635 
   636 		if ( bDeleteDevice )
   637 		{
   638 			delete m_pDevice;
   639 			m_pDevice = nullptr;
   640 		}
   641 	}
   642 
   643 private:
   644 	pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER;
   645 	int m_nRefCount = 0;
   646 	int m_nId = 0;
   647 	hid_device_info *m_pInfo = nullptr;
   648 	hid_device *m_pDevice = nullptr;
   649 	bool m_bIsBLESteamController = false;
   650 
   651 	pthread_mutex_t m_dataLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access m_vecData
   652 	hid_buffer_pool m_vecData;
   653 
   654 	// For handling get_feature_report
   655 	pthread_mutex_t m_cvLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access any variables below
   656 	pthread_cond_t m_cv = PTHREAD_COND_INITIALIZER;
   657 	bool m_bIsWaitingForOpen = false;
   658 	bool m_bOpenResult = false;
   659 	bool m_bIsWaitingForFeatureReport = false;
   660 	int m_nFeatureReportError = 0;
   661 	hid_buffer m_featureReport;
   662 
   663 public:
   664 	hid_device_ref<CHIDDevice> next;
   665 };
   666 
   667 class CHIDDevice;
   668 static pthread_mutex_t g_DevicesMutex = PTHREAD_MUTEX_INITIALIZER;
   669 static pthread_mutex_t g_DevicesRefCountMutex = PTHREAD_MUTEX_INITIALIZER;
   670 static hid_device_ref<CHIDDevice> g_Devices;
   671 
   672 static hid_device_ref<CHIDDevice> FindDevice( int nDeviceId )
   673 {
   674 	hid_device_ref<CHIDDevice> pDevice;
   675 
   676 	hid_mutex_guard l( &g_DevicesMutex );
   677 	for ( pDevice = g_Devices; pDevice; pDevice = pDevice->next )
   678 	{
   679 		if ( pDevice->GetId() == nDeviceId )
   680 		{
   681 			break;
   682 		}
   683 	}
   684 	return pDevice;
   685 }
   686 
   687 static void ThreadDestroyed(void* value)
   688 {
   689 	/* The thread is being destroyed, detach it from the Java VM and set the g_ThreadKey value to NULL as required */
   690 	JNIEnv *env = (JNIEnv*) value;
   691 	if (env != NULL) {
   692 		g_JVM->DetachCurrentThread();
   693 		pthread_setspecific(g_ThreadKey, NULL);
   694 	}
   695 }
   696 
   697 
   698 extern "C"
   699 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
   700 
   701 extern "C"
   702 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
   703 
   704 extern "C"
   705 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface );
   706 
   707 extern "C"
   708 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
   709 
   710 extern "C"
   711 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
   712 
   713 extern "C"
   714 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
   715 
   716 extern "C"
   717 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
   718 
   719 extern "C"
   720 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
   721 
   722 
   723 extern "C"
   724 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
   725 {
   726 	LOGV( "HIDDeviceRegisterCallback()");
   727 
   728 	env->GetJavaVM( &g_JVM );
   729 
   730 	/*
   731 	 * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
   732 	 * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
   733 	 */
   734 	if (pthread_key_create(&g_ThreadKey, ThreadDestroyed) != 0) {
   735 		__android_log_print(ANDROID_LOG_ERROR, TAG, "Error initializing pthread key");
   736 	}
   737 
   738 	if ( g_HIDDeviceManagerCallbackHandler != NULL )
   739 	{
   740 		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
   741 		g_HIDDeviceManagerCallbackClass = NULL;
   742 		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
   743 		g_HIDDeviceManagerCallbackHandler = NULL;
   744 	}
   745 
   746 	g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( thiz );
   747 	jclass objClass = env->GetObjectClass( thiz );
   748 	if ( objClass )
   749 	{
   750 		g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
   751 		g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
   752 		if ( !g_midHIDDeviceManagerOpen )
   753 		{
   754 			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing openDevice" );
   755 		}
   756 		g_midHIDDeviceManagerSendOutputReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendOutputReport", "(I[B)I" );
   757 		if ( !g_midHIDDeviceManagerSendOutputReport )
   758 		{
   759 			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendOutputReport" );
   760 		}
   761 		g_midHIDDeviceManagerSendFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendFeatureReport", "(I[B)I" );
   762 		if ( !g_midHIDDeviceManagerSendFeatureReport )
   763 		{
   764 			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendFeatureReport" );
   765 		}
   766 		g_midHIDDeviceManagerGetFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "getFeatureReport", "(I[B)Z" );
   767 		if ( !g_midHIDDeviceManagerGetFeatureReport )
   768 		{
   769 			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing getFeatureReport" );
   770 		}
   771 		g_midHIDDeviceManagerClose = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "closeDevice", "(I)V" );
   772 		if ( !g_midHIDDeviceManagerClose )
   773 		{
   774 			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing closeDevice" );
   775 		}
   776 		env->DeleteLocalRef( objClass );
   777 	}
   778 }
   779 
   780 extern "C"
   781 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
   782 {
   783 	LOGV("HIDDeviceReleaseCallback");
   784 	if ( env->IsSameObject( thiz, g_HIDDeviceManagerCallbackHandler ) )
   785 	{
   786 		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
   787 		g_HIDDeviceManagerCallbackClass = NULL;
   788 		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
   789 		g_HIDDeviceManagerCallbackHandler = NULL;
   790 	}
   791 }
   792 
   793 extern "C"
   794 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface )
   795 {
   796 	LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
   797 
   798 	hid_device_info *pInfo = new hid_device_info;
   799 	memset( pInfo, 0, sizeof( *pInfo ) );
   800 	pInfo->path = CreateStringFromJString( env, sIdentifier );
   801 	pInfo->vendor_id = nVendorId;
   802 	pInfo->product_id = nProductId;
   803 	pInfo->serial_number = CreateWStringFromJString( env, sSerialNumber );
   804 	pInfo->release_number = nReleaseNumber;
   805 	pInfo->manufacturer_string = CreateWStringFromJString( env, sManufacturer );
   806 	pInfo->product_string = CreateWStringFromJString( env, sProduct );
   807 	pInfo->interface_number = nInterface;
   808 
   809 	hid_device_ref<CHIDDevice> pDevice( new CHIDDevice( nDeviceID, pInfo ) );
   810 
   811 	hid_mutex_guard l( &g_DevicesMutex );
   812 	hid_device_ref<CHIDDevice> pLast, pCurr;
   813 	for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
   814 	{
   815 		continue;
   816 	}
   817 	if ( pLast )
   818 	{
   819 		pLast->next = pDevice;
   820 	}
   821 	else
   822 	{
   823 		g_Devices = pDevice;
   824 	}
   825 }
   826 
   827 extern "C"
   828 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
   829 {
   830 	LOGV( "HIDDeviceOpenPending() id=%d\n", nDeviceID );
   831 	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
   832 	if ( pDevice )
   833 	{
   834 		pDevice->SetOpenPending();
   835 	}
   836 }
   837 
   838 extern "C"
   839 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
   840 {
   841 	LOGV( "HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false" );
   842 	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
   843 	if ( pDevice )
   844 	{
   845 		pDevice->SetOpenResult( bOpened );
   846 	}
   847 }
   848 
   849 extern "C"
   850 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
   851 {
   852 	LOGV( "HIDDeviceDisconnected() id=%d\n", nDeviceID );
   853 	hid_device_ref<CHIDDevice> pDevice;
   854 	{
   855 		hid_mutex_guard l( &g_DevicesMutex );
   856 		hid_device_ref<CHIDDevice> pLast, pCurr;
   857 		for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
   858 		{
   859 			if ( pCurr->GetId() == nDeviceID )
   860 			{
   861 				pDevice = pCurr;
   862 
   863 				if ( pLast )
   864 				{
   865 					pLast->next = pCurr->next;
   866 				}
   867 				else
   868 				{
   869 					g_Devices = pCurr->next;
   870 				}
   871 			}
   872 		}
   873 	}
   874 	if ( pDevice )
   875 	{
   876 		pDevice->Close( false );
   877 	}
   878 }
   879 
   880 extern "C"
   881 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
   882 {
   883 	jbyte *pBuf = env->GetByteArrayElements(value, NULL);
   884 	jsize nBufSize = env->GetArrayLength(value);
   885 
   886 //	LOGV( "HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize );
   887 	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
   888 	if ( pDevice )
   889 	{
   890 		pDevice->ProcessInput( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
   891 	}
   892 
   893 	env->ReleaseByteArrayElements(value, pBuf, 0);
   894 }
   895 
   896 extern "C"
   897 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
   898 {
   899 	jbyte *pBuf = env->GetByteArrayElements(value, NULL);
   900 	jsize nBufSize = env->GetArrayLength(value);
   901 
   902 	LOGV( "HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize );
   903 	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
   904 	if ( pDevice )
   905 	{
   906 		pDevice->ProcessFeatureReport( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
   907 	}
   908 
   909 	env->ReleaseByteArrayElements(value, pBuf, 0);
   910 }
   911 
   912 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
   913 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
   914 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
   915 
   916 extern "C"
   917 {
   918 
   919 int hid_init(void)
   920 {
   921 	return 0;
   922 }
   923 
   924 struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
   925 {
   926 	struct hid_device_info *root = NULL;
   927 	hid_mutex_guard l( &g_DevicesMutex );
   928 	for ( hid_device_ref<CHIDDevice> pDevice = g_Devices; pDevice; pDevice = pDevice->next )
   929 	{
   930 		const hid_device_info *info = pDevice->GetDeviceInfo();
   931 		if ( ( vendor_id == 0 && product_id == 0 ) ||
   932 			 ( vendor_id == info->vendor_id && product_id == info->product_id ) )
   933 		{
   934 			hid_device_info *dev = CopyHIDDeviceInfo( info );
   935 			dev->next = root;
   936 			root = dev;
   937 		}
   938 	}
   939 	return root;
   940 }
   941 
   942 void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
   943 {
   944 	while ( devs )
   945 	{
   946 		struct hid_device_info *next = devs->next;
   947 		FreeHIDDeviceInfo( devs );
   948 		devs = next;
   949 	}
   950 }
   951 
   952 HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
   953 {
   954 	// TODO: Implement
   955 	return NULL;
   956 }
   957 
   958 HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
   959 {
   960 	LOGV( "hid_open_path( %s )", path );
   961 
   962 	hid_device_ref< CHIDDevice > pDevice;
   963 	{
   964 		hid_mutex_guard r( &g_DevicesRefCountMutex );
   965 		hid_mutex_guard l( &g_DevicesMutex );
   966 		for ( hid_device_ref<CHIDDevice> pCurr = g_Devices; pCurr; pCurr = pCurr->next )
   967 		{
   968 			if ( strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 ) 
   969 			{
   970 				hid_device *pValue = pCurr->GetDevice();
   971 				if ( pValue )
   972 				{
   973 					++pValue->m_nDeviceRefCount;
   974 					LOGD("Incrementing device %d (%p), refCount = %d\n", pValue->m_nId, pValue, pValue->m_nDeviceRefCount);
   975 					return pValue;
   976 				}
   977 
   978 				// Hold a shared pointer to the controller for the duration
   979 				pDevice = pCurr;
   980 				break;
   981 			}
   982 		}
   983 	}
   984 	if ( pDevice && pDevice->BOpen() )
   985 	{
   986 		return pDevice->GetDevice();
   987 	}
   988 	return NULL;
   989 }
   990 
   991 int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
   992 {
   993 	LOGV( "hid_write id=%d length=%u", device->m_nId, length );
   994 	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
   995 	if ( pDevice )
   996 	{
   997 		return pDevice->SendOutputReport( data, length );
   998 	}
   999 	return -1; // Controller was disconnected
  1000 }
  1001 
  1002 // TODO: Implement timeout?
  1003 int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
  1004 {
  1005 //	LOGV( "hid_read_timeout id=%d length=%u timeout=%d", device->m_nId, length, milliseconds );
  1006 	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
  1007 	if ( pDevice )
  1008 	{
  1009 		return pDevice->GetInput( data, length );
  1010 	}
  1011 	LOGV( "controller was disconnected" );
  1012 	return -1; // Controller was disconnected
  1013 }
  1014 
  1015 // TODO: Implement blocking
  1016 int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
  1017 {
  1018 	LOGV( "hid_read id=%d length=%u", device->m_nId, length );
  1019 	return hid_read_timeout( device, data, length, 0 );
  1020 }
  1021 
  1022 // TODO: Implement?
  1023 int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
  1024 {
  1025 	return -1;
  1026 }
  1027 
  1028 int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
  1029 {
  1030 	LOGV( "hid_send_feature_report id=%d length=%u", device->m_nId, length );
  1031 	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
  1032 	if ( pDevice )
  1033 	{
  1034 		return pDevice->SendFeatureReport( data, length );
  1035 	}
  1036 	return -1; // Controller was disconnected
  1037 }
  1038 
  1039 
  1040 // Synchronous operation. Will block until completed.
  1041 int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
  1042 {
  1043 	LOGV( "hid_get_feature_report id=%d length=%u", device->m_nId, length );
  1044 	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
  1045 	if ( pDevice )
  1046 	{
  1047 		return pDevice->GetFeatureReport( data, length );
  1048 	}
  1049 	return -1; // Controller was disconnected
  1050 }
  1051 
  1052 
  1053 void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
  1054 {
  1055 	LOGV( "hid_close id=%d", device->m_nId );
  1056 	hid_mutex_guard r( &g_DevicesRefCountMutex );
  1057 	LOGD("Decrementing device %d (%p), refCount = %d\n", device->m_nId, device, device->m_nDeviceRefCount - 1);
  1058 	if ( --device->m_nDeviceRefCount == 0 )
  1059 	{
  1060 		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
  1061 		if ( pDevice )
  1062 		{
  1063 			pDevice->Close( true );
  1064 		}
  1065 		else
  1066 		{
  1067 			delete device;
  1068 		}
  1069 		LOGD("Deleted device %p\n", device);
  1070 	}
  1071 
  1072 }
  1073 
  1074 int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
  1075 {
  1076 	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
  1077 	if ( pDevice )
  1078 	{
  1079 		wcsncpy( string, pDevice->GetDeviceInfo()->manufacturer_string, maxlen );
  1080 		return 0;
  1081 	}
  1082 	return -1;
  1083 }
  1084 
  1085 int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
  1086 {
  1087 	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
  1088 	if ( pDevice )
  1089 	{
  1090 		wcsncpy( string, pDevice->GetDeviceInfo()->product_string, maxlen );
  1091 		return 0;
  1092 	}
  1093 	return -1;
  1094 }
  1095 
  1096 int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
  1097 {
  1098 	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
  1099 	if ( pDevice )
  1100 	{
  1101 		wcsncpy( string, pDevice->GetDeviceInfo()->serial_number, maxlen );
  1102 		return 0;
  1103 	}
  1104 	return -1;
  1105 }
  1106 
  1107 int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
  1108 {
  1109 	return -1;
  1110 }
  1111 
  1112 HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
  1113 {
  1114 	return NULL;
  1115 }
  1116 
  1117 int hid_exit(void)
  1118 {
  1119 	return 0;
  1120 }
  1121 
  1122 }