Android Open Accessory on Galaxy Note 3

1.1k Views Asked by At

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

  1. /system/framework/com.android.future.usb­.accessory.jar
  2. /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.

1

There are 1 best solutions below

0
On

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.