I am making some experiments with Android Open Accessory Protocol (AOA) using various phones/tablets and Arduino DUE.
I need some help to try to find the cause of a different behaviour of a simple app based on AOA that reads data from an analog input pin and that changes the status of a digital output pin of the Arduino DUE.
All phones on which I am trying the app are not rooted and I have verified that all of them have the files
- /system/framework/com.android.future.usb.accessory.jar
- /system/etc/permissions/android.hardware.usb.accessory.xml
that, as long as I know, are the only two requested to give support to AOA.
The named app works well on a Samsung Galaxy S2 (Android version 4.1.2, kernel v3.0.31-1211311) and on a Galaxy Pocket Neo (Android version 4.1.2, kernel v3.0.15-1456085), but works only "partially" on a Samsung Galaxy Note 3 (Android version 4.3, kernel v3.4.0-2019540).
In more detail, when I connect the accessory to all of the specified phones (also the Galaxy Note 3), it is correctly recognized and the app associated to the Arduino sketch controls correctly (an all phones) the status of the digital output pin, but the Galaxy Note seems to don't be able to receive from Arduino and to display the message containing the result of the analog-to-digital conversion (two bytes).
The app is one of the tutorial projects of the UDOO development platform and the original post is here: http://www.udoo.org/ProjectsAndTutorials/android-and-arduino-on-udoo-bidirectional-communication/?portfolioID=1394
Here is the code of MainActivity.java:
package org.udoo.androidadkdemobidirect;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.udoo.androidadkdemobidirect.R;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
public class MainActivity extends Activity implements Runnable{
private static final String TAG = "UDOO_AndroidADKFULL";
private static final String ACTION_USB_PERMISSION = "org.udoo.androidadkdemobidirect.action.USB_PERMISSION";
private UsbManager mUsbManager;
private PendingIntent mPermissionIntent;
private boolean mPermissionRequestPending;
private Message m;
UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;
private static ToggleButton btn_LED = null;
private static TextView tv_adResult = null;
private boolean running = false;
// Receive the USB attached intent
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
openAccessory(accessory);
} else {
Log.d(TAG, "permission denied for accessory "+ accessory);
}
mPermissionRequestPending = false;
}
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null && accessory.equals(mAccessory)) {
closeAccessory();
}
}
}
};
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(mUsbReceiver, filter);
if (getLastNonConfigurationInstance() != null) {
mAccessory = (UsbAccessory) getLastNonConfigurationInstance();
openAccessory(mAccessory);
}
setContentView(R.layout.activity_main);
btn_LED = (ToggleButton) findViewById(R.id.toggleButtonLED);
tv_adResult = (TextView) findViewById(R.id.adResult);
}
@SuppressWarnings("deprecation")
@Override
public Object onRetainNonConfigurationInstance() {
if (mAccessory != null) {
return mAccessory;
} else {
return super.onRetainNonConfigurationInstance();
}
}
@Override
public void onResume() {
super.onResume();
if (mInputStream != null && mOutputStream != null) {
running = true;
return;
}
//open the accessory from the accessory list
UsbAccessory[] accessories = mUsbManager.getAccessoryList();
UsbAccessory accessory = (accessories == null ? null : accessories[0]);
if (accessory != null) {
if (mUsbManager.hasPermission(accessory)) {
openAccessory(accessory);
} else {
synchronized (mUsbReceiver) {
if (!mPermissionRequestPending) {
mUsbManager.requestPermission(accessory,mPermissionIntent);
mPermissionRequestPending = true;
}
}
}
} else {
Log.d(TAG, "mAccessory is null");
}
}
@Override
public void onPause() {
running = false;
super.onPause();
}
@Override
public void onDestroy() {
running = false;
closeAccessory();
unregisterReceiver(mUsbReceiver);
super.onDestroy();
}
// open the accessory and open the input and output stream from the descriptor
// start also the thread that reads from Arduino
private void openAccessory(UsbAccessory accessory) {
mFileDescriptor = mUsbManager.openAccessory(accessory);
if (mFileDescriptor != null) {
mAccessory = accessory;
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(this, "UDOO_ADK_readfrom");
running = true;
thread.start();
Toast.makeText(getApplicationContext(), "Accessory connected", Toast.LENGTH_SHORT).show();
Log.i(TAG, "openaccessory");
}
else {
Toast.makeText(getApplicationContext(), "Accessory not connected", Toast.LENGTH_SHORT).show();
}
}
// close the accessory
private void closeAccessory() {
Log.i(TAG, "closeaccessory");
try {
if (mFileDescriptor != null) {
mFileDescriptor.close();
}
} catch (IOException e) {
} finally {
mFileDescriptor = null;
mAccessory = null;
running = false;
}
}
// ToggleButton method - send message to Arduino
public void writeToAccessory(View v){
if (mAccessory != null) {
byte[] message = new byte[1];
message[0] = (byte) (btn_LED.isChecked()?1:0);
if (mOutputStream != null) {
try {
mOutputStream.write(message);
} catch (IOException e) {
Log.e(TAG, "write failed", e);
}
}
}
else
Toast.makeText(getApplicationContext(), "Accessory not connected", Toast.LENGTH_SHORT).show();
}
// Thread to read data from Arduino
public void run() {
int ret = 0;
byte[] buffer = new byte[4];
while (running) {
try {
ret = mInputStream.read(buffer);
} catch (IOException e) {
break;
}
m = Message.obtain(mHandler);
if (ret != 0) {
m.arg1 = (int) ((buffer[0] & 0xff) << 8) + (buffer[1] & 0xff);
ret = 0;
}
mHandler.sendMessage(m);
}
}
static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
final int temp = msg.arg1;
tv_adResult.setText(String.format("%4d (= %.2f V)", temp, 3.3/1024*temp));
}
};
}
I have also verified that this strange behaviour persists also for other apps based on the AOA Protocol, that is: the Galaxy Note 3 sends correctly the output messages to the accessory but doesn't read the input messages from it.
I am unable to find where the problem is and I would ask the StackOverflow Community to give me some hint.
Any help will be appreciated.
I am not sure if this helps, but you could try to disable USB Debugging from the Developer Options. Some phones have issue with the debugging mode on.